Introduction

Escherichia coli is a gram negative bacterium and it is the most studied bacterial model organism for many reasons like its ability to grow fast on cheap media and the fact that it can be genetically manipulated and modified much easier with respect to eukaryotic organisms like yeast or animals or even other bacteria. When an organism is as studied as E. coli the amount of data available about it can be massive, for this reason we would like to perform a bioinformatic analysis to extract knowledge from on one of the many RNA-seq dataset available for E. coli.

This project aims to establish a proxy for determining the activity of transcription factors based on the expression levels of the genes regulated by those factors. In prokaryotes genes with related functions are encoded together within the same genome block, which is called operon, and they are usually transcribed together in a, so called, polycistronic transcript because they are under the control of the same promoter.

The null hypothesis posits that the abundance of transcripts serves as a reliable proxy for activity of the regulator. However, this assumption may not hold for all transcription factors: some factors are active only when phosphorylated, meaning that their transcripts may be constitutively expressed while their activity depends on phosphorylation, correlating instead with the activity of the corresponding kinase.

We will also focus on developing a general model to relate transcription factor activity to transcript abundance. Significant deviations from this model could indicate transcription factors whose activity is not accurately reflected by transcript levels. One challenge of this approach is the fact that genes of the same operon may be highly co-expressed together which is a problem for many models like linear regression, so we will try to address it during our analysis.

Table of Contents

Here are the steps that we performed in our analysis

  1. Introduction

  2. Library loading

  3. Pre-processing

  4. Exploratory Analysis

  5. Modelling -cAMP Receptor Protein (crp)

Library loading

library(readr)
library(matrixStats)
library(dplyr)
library(ggplot2)
library(grid)
library(gridExtra)
library(caret)
library(glmnet)
library(igraph)
library(vip)
library(ggrepel)

Pre-processing

Let’s start importing the dataset from PreciseDB which is an RNA-seq compendium for Escherichia coli from the Systems Biology Research group at UC San Diego which contains the expression levels for 3,923 genes in E. coli for 278 different condition. The approach they used was culturing bacteria on many different media and they measured gene expression values then they proceeded to knock-out many different genes and cultured again on different media to measure expression again because they were expecting variations of expression; for example, they knocked out a gene for iron metabolism on a medium containing iron and this for sure affects the expression levels of the E.coli culture because the organism is not able to manage iron correctly anymore.

# Import file 
log_tpm <- read.csv("log_tpm_full.csv", row.names = 1)
log_tpm

Next we want to perform some pre-processing. First we want to exclude:

For the nature of our analysis we are not interested in the conditions in which a gene is knocked-out because the influence of the knock-out gene may be huge on its related pathways, for this reason we excluded them because we want unbiased results. Researchers of PreciseDB also collected expression data about many gene isoforms, the problem is that the collection of isoforms was not complete and there was not a reliable way to decide which isoform to consider: considering all of them during the preprocessing would have resulted in having unmappable bnumbers and duplicate genes so we removed them from the dataset before proceeding.

# Exclude condition with knockout genes
log_tpm <- log_tpm[, -grep("_del", colnames(log_tpm))]

# Drop genes with isoform
genes_with_isoforms <- grep("_\\d+$", rownames(log_tpm), value = TRUE)

log_tpm <- subset(log_tpm, !(rownames(log_tpm) %in% genes_with_isoforms))
dim(log_tpm)
[1] 4257  188

Now we can proceed to import the regulatory network from RegulonDB that reports the target genes for each regulator. Each row of this dataset represents an edge in a regulatory network graph and contains informations about the interaction of which the most important one is probably if the interaction is positive or negative.

regulator <- read.table(file="tableDataReg.csv",
                        header=TRUE, 
                        sep=",")
regulator

We decided to eliminate:

regulator <- regulator[which(regulator[, 2] != "ppGpp"), ]

w <- which(trimws(regulator[,7])=="W")
if(length(w)>0){
  regulator <- regulator[-w,]
}

w <- which(trimws(regulator[,7])=="?")
if(length(w)>0){
  regulator <- regulator[-w,]
}

nrow(regulator)
[1] 4229

There is a discrepancy between the gene identifiers present in the regulatory network obtained from RegulonDB and those in the PreciseDB dataset. While the regulatory network uses gene names, our dataset contained identifiers in the form of bnumbers. So it’s better to convert bnumbers to gene names to facilitate comparison and downstream analysis. In addition we decided to:

# Loading the file from ECOcyc which contain the mapping bnum-genename
map_bnum <- read.delim("mapbnum.txt", header = TRUE)

# Keep only the useful columns
map_bnum <- map_bnum[c("Gene.Name", "Accession.1")]

# Map between our dataset and the file of ecocyc
log_tpm$gene_number <- rownames(log_tpm)
log_tpm <- merge(log_tpm, map_bnum, by.x = "gene_number", by.y = "Accession.1", all.x = TRUE)

# Rearrange the dataset
log_tpm <- log_tpm[, c("Gene.Name", setdiff(names(log_tpm), "Gene.Name"))]

# Removing unmapped genes bnumber
log_tpm <- subset(log_tpm, !is.na(Gene.Name))

# Removing duplicate genes (it also has all expression values equal 0 so very bad)
log_tpm <- subset(log_tpm, !(log_tpm$Gene.Name == "insI2"))

# Setting rownames and dropping the first 2 columns
rownames(log_tpm) <- log_tpm$Gene.Name
log_tpm <- log_tpm[,3:ncol(log_tpm)]

# Transpose log_tpm in order to have genes as columns and conditions as rows
log_tpm <- t(log_tpm)

Exploratory Analysis

We would like to verify if we can use one the four major summary statistics which are mean, median, maximum and minimum of expression of each gene to model the relationship between the targets and their regulator. Now let’s analyze the distribution of these statistics to understand which is the best value to choose, if any, then we also want check if they follow a Gaussian distribution using the Shapiro–Wilk test which computes the W statistics as follows:

\[ W = \dfrac{\big(\sum^n _ {i=1} a_i x_{(i)} \big ) ^2}{\sum^n _ {i=1} (x_i - \bar{x})^2} \]

Then this W value is used for the hypothesis testing that follows:

\[ H_o: \text{a sample } x_1, \cdots, x_n \text{ is drawn from a normally distributed population.} \\ H_a: \text{a sample } x_1, \cdots, x_n \text{ is not drawn from a normally distributed population.} \]

If the test returns a p-value lower then the threshold 0.05 it means that the data do not follow a normal distribution.

# Histograms of Summary Statistics

# MEAN
log_tpm_mean <- data.frame(value = apply(log_tpm, 2, mean))
mean_hist <- ggplot(log_tpm_mean, aes(x = value)) +
                    geom_histogram(binwidth = 0.5, fill = "skyblue", 
                                   color = "black", bins = 100) +
                    labs(x = "Mean log-TPM", y = "Frequency") +
                    theme_minimal() +
                    theme(panel.grid.major = element_blank(), 
                          panel.grid.minor = element_blank())

# MEDIAN
log_tpm_median <- data.frame(value = apply(log_tpm, 2, median))
median_hist <- ggplot(log_tpm_median, aes(x = value)) +
                      geom_histogram(binwidth = 0.5, fill = "lightgreen", 
                                     color = "black", bins = 100) +
                      labs(x = "Median log-TPM", y = "Frequency") +
                      theme_minimal() +
                      theme(panel.grid.major = element_blank(), 
                            panel.grid.minor = element_blank())

# MAX
log_tpm_max <- data.frame(value = apply(log_tpm, 2, max))
max_hist <- ggplot(log_tpm_max, aes(x = value)) +
                   geom_histogram(binwidth = 0.5, fill = "lavender", 
                                  color = "black", bins = 100) +
                   labs(x = "Max log-TPM", y = "Frequency") +
                   theme_minimal() +
                   theme(panel.grid.major = element_blank(), 
                         panel.grid.minor = element_blank())

# MIN
log_tpm_min <- data.frame(value = apply(log_tpm, 2, min))
min_hist <- ggplot(log_tpm_min, aes(x = value)) +
                   geom_histogram(binwidth = 0.5, fill = "lightpink", 
                                  color = "black", bins = 100) +
                   labs(x = "Min log-TPM", y = "Frequency") +
                   theme_minimal() +
                   theme(panel.grid.major = element_blank(), 
                         panel.grid.minor = element_blank())

# Final Plot
grid.arrange(mean_hist, median_hist, max_hist, min_hist, nrow = 2, ncol = 2,
             top = textGrob("Histograms of Summary Statistics", 
                            gp=gpar(fontsize=16)))

#MEAN
shapiro.test(log_tpm_mean$value) #not gaussian

    Shapiro-Wilk normality test

data:  log_tpm_mean$value
W = 0.98717, p-value < 2.2e-16
#MEDIAN
shapiro.test(log_tpm_median$value) #not gaussian

    Shapiro-Wilk normality test

data:  log_tpm_median$value
W = 0.98578, p-value < 2.2e-16
#MAXIMUM
shapiro.test(log_tpm_max$value) #not gaussian

    Shapiro-Wilk normality test

data:  log_tpm_max$value
W = 0.99611, p-value = 3.889e-09
#MINIMUM
shapiro.test(log_tpm_min$value) #not gaussian

    Shapiro-Wilk normality test

data:  log_tpm_min$value
W = 0.91615, p-value < 2.2e-16

Based on the results of the Shapiro-Wilk tests conducted on the four distributions and their respective histograms, we can conclude that the distributions do not follow to a Gaussian (normal) distribution.
Consequently, this implies that choosing measures such as the mean, median, maximum, or minimum may not match the linear regression assumption that the data follows a Gaussian (normal) distribution, for this reason it is not possible to use one of these statistics as a predictor in our model because none of them is normally distributed but the biggest problem is that we would train a model on a single sample and it does not make any sense. In the next section about modelling we will try instead to take the mean of all the targets in every condition and fit a model with 1 feature (mean of the targets) and as many samples as the conditions that we have.

Now we plot some histograms to have a better idea on the number of positive, negative and total interactions of each regulator and again we perform a Shapiro–Wilk test for each one of them to check for normality.

# Divide positive and negative regulation
positive_reg <- regulator[regulator$X6.function == "+",]
negative_reg <- regulator[regulator$X6.function == "-",]

# Identify the different regulators in the dataset
unique_regulators <- unique(regulator$X3.RegulatorGeneName)

pos_counts <- c()
neg_counts <- c()

# Count how many genes are regulated by each regulator
for(reg in unique_regulators){
  pos_counts <- c(pos_counts, 
                  count(positive_reg[positive_reg$X3.RegulatorGeneName == reg,]))
  neg_counts <- c(neg_counts, 
                  count(negative_reg[negative_reg$X3.RegulatorGeneName == reg,]))
}

pos_counts <- unlist(pos_counts)
names(pos_counts) <- unique_regulators

neg_counts <- unlist(neg_counts)
names(neg_counts) <- unique_regulators

pos_counts <- data.frame(value = pos_counts)
neg_counts <- data.frame(value = neg_counts)
total_counts <- data.frame(value = pos_counts$value + neg_counts$value)

# Histograms of how many genes are regulated by each gene

# Positive Counts Histogram
pos_counts_hist <- ggplot(pos_counts, aes(x = value)) +
                          geom_histogram(binwidth = 10, fill = "skyblue", 
                                         color = "black", bins = 20) +
                          labs(x = "Positive Regulations Count", y = "Frequency") +
                          theme_minimal() +
                          theme(panel.grid.major = element_blank(), 
                                panel.grid.minor = element_blank())

# Negative Counts Histogram
neg_counts_hist <- ggplot(neg_counts, aes(x = value)) +
                          geom_histogram(binwidth = 10, fill = "lightgreen", 
                                         color = "black", bins = 20) +
                          labs(x = "Negative Regulations Count", y = "Frequency") +
                          theme_minimal() +
                          theme(panel.grid.major = element_blank(), 
                                panel.grid.minor = element_blank())

# Total Counts Histogram
total_counts_hist <- ggplot(total_counts, aes(x = value)) +
                            geom_histogram(binwidth = 10, fill = "lightpink", 
                                           color = "black", bins = 20) +
                            labs(x = "Total Regulations Count", y = "Frequency") +
                            theme_minimal() +
                            theme(panel.grid.major = element_blank(), 
                                  panel.grid.minor = element_blank())

# Final Plot
grid.arrange(pos_counts_hist, neg_counts_hist, total_counts_hist, ncol = 1)

# Positive Interactions Counts normality test
shapiro.test(pos_counts$value) #not gaussian

    Shapiro-Wilk normality test

data:  pos_counts$value
W = 0.29429, p-value < 2.2e-16
# Negative Interactions Counts normality test
shapiro.test(neg_counts$value) #not gaussian

    Shapiro-Wilk normality test

data:  neg_counts$value
W = 0.25872, p-value < 2.2e-16
# Total Interactions Counts normality test
shapiro.test(total_counts$value) #not gaussian

    Shapiro-Wilk normality test

data:  total_counts$value
W = 0.29589, p-value < 2.2e-16

These histograms show how many genes are regulated by how many regulators, the horizontal axis indicates how many genes are controlled by the regulator and the vertical axis indicates how many regulators have that number of interactions; for example there are more than 150 regulators that positively interact with 0 genes, this is not surprising because we removed weak and unknown interactions and we can also see that there is a gene that negatively controls a number of genes close to 400. There are slightly more negative regulators that interact with at least one gene then positive. In addition we observe that majority of genes does not interact with other genes but there are some exceptions like the gene crp which positively interacts with 310 other genes. We can also note that none of these is normally distributed.

Network analysis

# Creating adj matrix to plot the network
edge_list <- cbind(RegulatorName = regulator$X3.RegulatorGeneName, 
                   Target = regulator$X5.regulatedName)
graph <- graph_from_edgelist(edge_list)
layout <- layout_with_fr(graph, niter=100)


# Visualizzazione 3D della network
plot.igraph(graph, layout=layout, vertex.label=NA, vertex.size=3, edge.arrow.size=0.2, edge.curved=TRUE, main="E.coli Network", xlim=c(-0.5, 0.5), ylim=c(-1, 1))

Modelling

In order to find a mathematical model able to describe the complexity of this biological problem we would like to start with simpler cases and look for what might be the best approach for them then we will apply this approach to each regulator. For this reason we will individually perform the modelling on the positive interactions of 2 genes to see which model can be suitable to be applied to all the genes; We chose the genes crp and gene da scegliere

cAMP Receptor Protein (crp)

The protein CRP is a global transcription regulator, which plays a major role in carbon catabolite repression (CCR) as well as other processes because it can act as an activator, repressor, coactivator or corepressor. CRP binds cyclic AMP (cAMP) which allosterically activates it, this means that its activity should not be influenced by the activity of any kinase, then it binds to its DNA binding site to directly regulate the transcription of about 300 genes in about 200 operons and indirectly regulate the expression of about half the genome.

Simple Linear Regression

The first thing we want to try, as anticipated in the previous section, is taking the mean of all the targets in every condition and fit a linear regression model with only this one feature. Let’s start to take the data about crp expression and the expression of its targets

# Expression of crp
crp_exp <- log_tpm[,colnames(log_tpm) == "crp"]

# List of positively regulated targets by crp
crp_target <- positive_reg[positive_reg$X3.RegulatorGeneName == "crp",]

# Expression of the targets
crp_target_exp <- log_tpm[,colnames(log_tpm) %in% crp_target$X5.regulatedName]

Now we take the simple mean of all the genes of each condition, in this way we will obtain as many means as the number of samples (188) and we fit a linear regression model using this mean as the sole feature. Then we put together a dataframe with all the data needed.

# Apply the mean to each row of the dataset (mean of each condition)
crp_target_mean_conditions <- apply(crp_target_exp, 1, mean)

# Creating the training dataframe
crp_mean_train <- data.frame(crp = unlist(crp_exp), 
                             targets = unlist(crp_target_mean_conditions))

All the models that we will fit in this project will be validated with 10-fold cross-validation, cross-validation is a simple and widely used sampling technique for estimating prediction error to assess how the model would act against unseen data. It is often done when there is not enough data to set aside a validation set and it has many advantages like preventing overfitting by providing a more robust estimate of the model’s performances and allowing model selection and hyperparameter tuning while being at the same time data efficient because all the data is used for training and testing the model. In the case of linear regression the purpose of cross-validation is only to get a robust estimate of the performances but in the next sections we will try to use it for model selection and hyperparameter tuning. The rationale of 10-fold cross-validation is very simple because it divides the dataset in 10 folds then it uses 9 for training and the last one for testing, this procedure is repeated other 9 times by changing the testing fold and by using the remaining ones for training.

As you can see from the picture above at every iteration the procedure computes an error metric and at the end it will take the average of all the metrics to gave us a more robust estimate of real testing error. The caret package is the R equivalent of Python scikit-learn and it allows to very easily set up the training of Machine Learning models and it also provide a very nice way to set-up a cross-validation in order to have more reliable models!

## 10-fold CV
fit_control <- trainControl(
  method = "repeatedcv",
  number = 10,
  ## repeated ten times
  repeats = 10)

Let’s get back to linear regression, using one feature we are in a setting for the use of simple linear regression which is a model that assumes that there is a linear relationship between \(X\) and \(Y\), in this case \(Y\) is the expression of our regulator crp and \(X\) is the mean of all its targets. The relationship between \(X\) and \(Y\) is modelled as follows:

\(Y \approx \beta_0 + \beta_1 X\)

To find a solution for this problem the Least Squares Method is employed so we know that \(\hat{y_i} = \hat{\beta_0} + \hat{\beta_1}x_i\) is a prediction from our model and \(e_i = y_i - \hat{y_i}\) is the residual error, that is the difference between the actual value \(y_i\) and the predicted one \(\hat{y_i}\).
Least squares finds a solution that minimizes the residual sum of squares of all training data, RSS.

\(RSS = e_1^2 + e_2^2 + \dots + e_N^2 = \sum_i (y_i - \hat{y_i})^2 = \sum_{i=1}^N (y_i - \hat{\beta_0} - \hat{\beta_1}x_i)^2\)

Now we compute the partial derivatives of RSS with respect to the model parameters \(\hat{\beta_0}\) and \(\hat{\beta_1}\) and equate them to 0. The RSS function is globally convex, meaning that there exist a single stationary point which is a minimum for this reason we don’t need to compute the second derivative to find the global minumun and we can directly solve the equations obtained before with respect to the parameters in this way:

\(\hat{\beta}_{1}=\frac{\sum_{i=1}^{n}\left(x_{i}-\overline{x}\right)\left(y_{i}-\overline{y}\right)}{\sum_{i=1}^{n}\left(x_{i}-\overline{x}\right)^{2}} \quad\quad\) \(\hat{\beta}_{0}=\overline{y}-\hat{\beta}_{1} \overline{x}\)

where \(\bar{x}\) is the mean of the means of expression of crp targets in all the conditions and \(\bar{y}\) is the mean expression of crp.

As anticipated before the fitting of linear regression with 10-fold cross-validation with caret can be achieved in a very simple way and with few lines of code

#cv is a random procedure so we set a random seed for reproducibility
set.seed(123)

crp_lm_mean <- train(crp ~ targets, 
                     data = crp_mean_train,
                     method = "lm",
                     trControl = fit_control)
crp_lm_mean
Linear Regression 

188 samples
  1 predictor

No pre-processing
Resampling: Cross-Validated (10 fold, repeated 10 times) 
Summary of sample sizes: 170, 168, 169, 171, 169, 169, ... 
Resampling results:

  RMSE       Rsquared    MAE      
  0.3720215  0.03536782  0.2895687

Tuning parameter 'intercept' was held constant at a value of TRUE

The coefficient of determination, or \(R2\), is a measure that provides information about the goodness of fit of a model. In other word it is a value that tells us about the variance of the data explained by the model, it is computed using the following formula.

\[ R^2 = \dfrac{\sum _ {i} \big(\hat{y_i} -\bar{y} \big)^2}{\sum _ {i} (y_i -\bar{y})^2} \]

\(R^2\) values close to 1 tell us that the model is able to explain almost 100% of the variance of the data indicating that the model fitted very well the data and values close to 0 tell the opposite, in this case we got an \(R^2\) = 0.03536782 which is a very low value and this means that the regression was not able to model the expression of the gene crp using the mean of its target. This result can be further explained by looking if some assumption of linear regression was not matched, more in detail we will now look for homoschedasticity and normality of the residuals.

# scatterplot of the data with the regression line
crp_lm_mean_scatterplot <- ggplot(crp_mean_train, aes(x = targets, y = crp)) +
                           ggtitle("Scatterplot of targets mean and crp") +
                           theme(plot.title = element_text(hjust = 0.5)) + 
                           xlab("Targets Mean in each Condition") + 
                           ylab("crp expression in log(TPM)") + 
                           geom_point(size = 2) + 
                           geom_abline(intercept = crp_lm_mean$finalModel$coefficients[[1]],
                                       slope = crp_lm_mean$finalModel$coefficients[[2]],
                                       color = "red",
                                       linewidth = 1)  

# Dataframe containing fitted values and the residuals
crp_res <- data.frame(fitted.values = crp_lm_mean$finalModel$fitted.values,
                      residuals = crp_lm_mean$finalModel$residuals)

# Residuals scatterplot
crp_lm_mean_residuals <-  ggplot(crp_res, aes(x = fitted.values, y = residuals)) +
                          ggtitle("Residuals plot") +
                          theme(plot.title = element_text(hjust = 0.5)) + 
                          xlab("Fitted Values") +
                          ylab("Residuals") + 
                          geom_point() +
                          geom_hline(yintercept = 0, color = "red")

# Final plot
grid.arrange(crp_lm_mean_scatterplot, crp_lm_mean_residuals, nrow = 1)

On the left you can see the scatterplot of the targets mean in each condition as the independent variable and the value of expression of crp in \(log(TPM)\) as the dependent variable with the regression line predicted by the model above drawn; on the right you can see the scatterplots of the residuals and we can notice just by looking that one assumption of linear regression is not matched: the variance of the residuals is not constant which means that the homeschedasticity of the residuals assumption is not met. Now let’s look for the normality of the residual to definitely.

crp_lm_mean_qq <- ggplot(crp_res, aes(sample=residuals)) +
                  ggtitle("Residuals QQ-plot") +
                  theme(plot.title = element_text(hjust = 0.5)) +
                  xlab("Theoretical Quantile") +
                  ylab("Residuals Quantiles") + 
                  stat_qq() + 
                  stat_qq_line(color = "red")
crp_lm_mean_qq

shapiro.test(crp_res$residuals)

    Shapiro-Wilk normality test

data:  crp_res$residuals
W = 0.98452, p-value = 0.03616

By looking at the QQ-plot we can see that the points are on the line for the most part but they are skewed at the tails and considering a p-value threshold of 0.05 for the normality test we can confidently say that the residuals are not normally distributed: another important assumption of linear regression is not matched. Considering all the elements discussed before we conclude that this simplicistic model is not able to describe the complex relationship between a regulator and its targets.

Multivariate Linear Regression

Since only one variable was not enough to explain the relationship between the regulator and its targets we will now try to extend linear regression in order to include more variables in the model. Similarly to simple linear regression, we want to find the values for the model’s parameters that minimize the residual sum of squares. The mathematical formulation of a multivariate linear regression is the following:

\[\hat{y}=\hat{\beta}_{0}+\hat{\beta}_{1} x_{1}+\hat{\beta}_{2} x_{2}+\cdots+\hat{\beta}_{p} x_{p}\]

The least squares solution for this problem involves matrix multiplication and the use of the, so called, pseudo-inverse to compute the \({\beta}\) coefficients then it uses them to compute the model’s predictions \(\hat{y}\) by multiplying the vector of coefficients \(\hat{\beta}\) with the matrix of the predictors \(X\).

\(\hat{\beta}=\left(\mathbf{X}^{T} \mathbf{X}\right)^{-1} \mathbf{X}^{T} \mathbf{y} \quad\quad\) with \(X\) a \(N \times (p+1)\) matrix, \(y\) a \(N \times 1\) vector

\(\hat{\mathbf{y}}=\mathbf{X} \hat{\beta}=\mathbf{X}\left(\mathbf{X}^{T} \mathbf{X}\right)^{-1} \mathbf{X}^{T} \mathbf{y} \quad\quad\) with \(\hat{y}\) the predicted value for each sample

The first step is creating a dataframe containing the expression of crp and of all its targets.

crp_exp <- data.frame(crp = crp_exp)
crp_full <- data.frame(cbind(crp_exp, crp_target_exp))

Then we train a model using all the target genes as predictors for crp expression with no further pre-processing and we cross-validate it.

crp_lm
Linear Regression 

188 samples
300 predictors

No pre-processing
Resampling: Cross-Validated (10 fold, repeated 10 times) 
Summary of sample sizes: 170, 168, 169, 171, 169, 169, ... 
Resampling results:

  RMSE      Rsquared    MAE     
  9.858278  0.09793242  7.807553

Tuning parameter 'intercept' was held constant at a value of TRUE

As you can see the \(R^2\) of this model is still not satisfying at all and the RMSE is also very high: the Root Mean Square Error (RMSE) is a metric that describes the average distance between the predicted values and the real values, which means that a value of RMSE = 9.858278 is very high in the context of gene expression because we are working in logarithmic scale. The reason why this model is so bad is directly told us by caret with a warning message (here suppressed) which says that the model was not able to obtain a lot of \(\beta\) coefficients because our predictor variables are correlated and an important assumption of linear regression is the independence of the predictor variables. This strong correlation between our variables is expected because they are all genes controlled by our regulator crp so it makes very much sense that they are correlated and it is also one of the reasons of this analysis. In order to remove this multi-collinearity we will try 2 main approaches: feature selection and dimensionality reduction.

Lasso Regression

The first approach we want to try is the Lasso Regression that is essentially multivariate linear regression but with the introduction of a penalization term which regularizes the estimates of the coefficients. This method is part of the family of the shrinkage methods and its main advantage is that it can force some of the coefficients of the model to be equal to 0 which means that it is also an effective way to perform feature selection. For this purpose Lasso minimizes the following function:

\[ \sum^n _{i=1} (y_i - \beta_0 - \sum^p _{j = 1}\beta_jx_ij)^2 + \lambda\sum^p_{j = 1}|\beta_j| \]

Here p is the number of predictors (number of targets of crp) and \(\lambda\) is a tunable hyperparameter which tells us how much we can “spend” in terms of coefficients, the right value of \(\lambda\) forces some coefficients to 0 and perform feature selection. The best value for \(\lambda\) has to be found with cross-validation for hyperparameter selection, it is interesting to note that if \(\lambda\) = 0 this minimization is simply the least squares method. Another important thing to do is to scale our data because Lasso is a method sensible to the scale, luckily caret can do this very easily using the preProcess argument in the train function.

# Tuning grid for Lasso 
tune_grid_lasso <- expand.grid(alpha = 1, #tell the function to perform lasso
                               lambda = 10^(-5:5)) #values of lambda to try

# Lasso training, cv and hyperparameter tuning
crp_lasso <- train(crp ~., 
                   data = crp_full,
                   method = "glmnet",
                   preProcess = c("center", "scale"), #this basically transform in Z scores
                   trControl = fit_control,
                   tuneGrid = tune_grid_lasso)
crp_lasso
glmnet 

188 samples
300 predictors

Pre-processing: centered (300), scaled (300) 
Resampling: Cross-Validated (10 fold, repeated 10 times) 
Summary of sample sizes: 168, 169, 169, 169, 170, 169, ... 
Resampling results across tuning parameters:

  lambda  RMSE       Rsquared   MAE      
  1e-05   0.1758236  0.7809450  0.1365638
  1e-04   0.1758236  0.7809450  0.1365638
  1e-03   0.1758236  0.7809450  0.1365638
  1e-02   0.1964024  0.7263848  0.1532794
  1e-01   0.3188542  0.4128588  0.2478034
  1e+00   0.3710368        NaN  0.2889244
  1e+01   0.3710368        NaN  0.2889244
  1e+02   0.3710368        NaN  0.2889244
  1e+03   0.3710368        NaN  0.2889244
  1e+04   0.3710368        NaN  0.2889244
  1e+05   0.3710368        NaN  0.2889244

Tuning parameter 'alpha' was held constant at a value of 1
RMSE was used to select the optimal model using the smallest value.
The final values used for the model were alpha = 1 and lambda = 0.001.

For the tuning of \(\lambda\) we tried powers of 10 from \(10^{-5}\) to \(10^5\) and as you can see for values greater than \(10^{-1}\) the model was not able to correctly converge (caret gave a warning we suppressed) for the same reasons of linear regression discussed before but for smaller values of \(\lambda\) they were able to converge and obtain good \(R^2\), finally with 10-fold cross-validation (results visually shown with the plot below) we chose \(\lambda = 10^{-3}\) as the final hyperparameter and the RMSE of this model is also very acceptable if we think in logarithmic scale in the context of gene expression.

cv_lasso_plot <-  ggplot(data = crp_lasso$results, aes(x = log(lambda), y = RMSE, group=1)) +
                  ggtitle("Cross-Validation Results") +
                  theme(plot.title = element_text(hjust = 0.5)) +
                  xlab("log(λ)") + 
                  ylab("RMSE (Repeated Cross-Validation)") + 
                  geom_line(color="blue")+
                  geom_point()

cv_lasso_plot

Let’s now take a look to the coefficients of the model

plot(crp_lasso$finalModel, xvar = "lambda", xlab= "log(λ)")

In this messy plot each line represents how the estimated coefficient \(\hat\beta\) of a gene changes with respect to the value of \(\lambda\), as you can see the majority of these lines go to 0 before \(log(\lambda) = -3\) so these genes have their coefficient set to 0 while the remaining genes have a coefficient different from 0.
Now we know that Lasso may be a reliable method for the modelling of our regulator-target relationship but it is heavily dependent on the value of \(\lambda\) chosen which might be different for other regulators, when we will analize all the genes we will try to verify this possibility.

Principal Component Regression

LS0tDQp0aXRsZTogIkJpb1Byb3h5Ig0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCg0KIyBJbnRyb2R1Y3Rpb24NCg0KKioqRXNjaGVyaWNoaWEgY29saSoqKiBpcyBhIGdyYW0gbmVnYXRpdmUgYmFjdGVyaXVtIGFuZCBpdCBpcyB0aGUgbW9zdCBzdHVkaWVkIGJhY3RlcmlhbCBtb2RlbCBvcmdhbmlzbSBmb3IgbWFueSByZWFzb25zIGxpa2UgaXRzIGFiaWxpdHkgdG8gZ3JvdyBmYXN0IG9uIGNoZWFwIG1lZGlhIGFuZCB0aGUgZmFjdCB0aGF0IGl0IGNhbiBiZSBnZW5ldGljYWxseSBtYW5pcHVsYXRlZCBhbmQgbW9kaWZpZWQgbXVjaCBlYXNpZXIgd2l0aCByZXNwZWN0IHRvIGV1a2FyeW90aWMgb3JnYW5pc21zIGxpa2UgeWVhc3Qgb3IgYW5pbWFscyBvciBldmVuIG90aGVyIGJhY3RlcmlhLiBXaGVuIGFuIG9yZ2FuaXNtIGlzIGFzIHN0dWRpZWQgYXMgKkUuIGNvbGkqIHRoZSBhbW91bnQgb2YgZGF0YSBhdmFpbGFibGUgYWJvdXQgaXQgY2FuIGJlIG1hc3NpdmUsIGZvciB0aGlzIHJlYXNvbiB3ZSB3b3VsZCBsaWtlIHRvIHBlcmZvcm0gYSBiaW9pbmZvcm1hdGljIGFuYWx5c2lzIHRvIGV4dHJhY3Qga25vd2xlZGdlIGZyb20gb24gb25lIG9mIHRoZSBtYW55IFJOQS1zZXEgZGF0YXNldCBhdmFpbGFibGUgZm9yICpFLiBjb2xpKi4NCg0KVGhpcyBwcm9qZWN0IGFpbXMgdG8gZXN0YWJsaXNoIGEgKipwcm94eSoqIGZvciBkZXRlcm1pbmluZyB0aGUgYWN0aXZpdHkgb2YgdHJhbnNjcmlwdGlvbiBmYWN0b3JzIGJhc2VkIG9uIHRoZSBleHByZXNzaW9uIGxldmVscyBvZiB0aGUgZ2VuZXMgcmVndWxhdGVkIGJ5IHRob3NlIGZhY3RvcnMuIEluIHByb2thcnlvdGVzIGdlbmVzIHdpdGggcmVsYXRlZCBmdW5jdGlvbnMgYXJlIGVuY29kZWQgdG9nZXRoZXIgd2l0aGluIHRoZSBzYW1lIGdlbm9tZSBibG9jaywgd2hpY2ggaXMgY2FsbGVkICoqb3Blcm9uKiosIGFuZCB0aGV5IGFyZSB1c3VhbGx5IHRyYW5zY3JpYmVkIHRvZ2V0aGVyIGluIGEsIHNvIGNhbGxlZCwgKipwb2x5Y2lzdHJvbmljIHRyYW5zY3JpcHQqKiBiZWNhdXNlIHRoZXkgYXJlIHVuZGVyIHRoZSBjb250cm9sIG9mIHRoZSBzYW1lIHByb21vdGVyLg0KDQpUaGUgbnVsbCBoeXBvdGhlc2lzIHBvc2l0cyB0aGF0IHRoZSAqYWJ1bmRhbmNlIG9mIHRyYW5zY3JpcHRzIHNlcnZlcyBhcyBhIHJlbGlhYmxlIHByb3h5IGZvciBhY3Rpdml0eSBvZiB0aGUgcmVndWxhdG9yKi4gSG93ZXZlciwgdGhpcyBhc3N1bXB0aW9uIG1heSBub3QgaG9sZCBmb3IgYWxsIHRyYW5zY3JpcHRpb24gZmFjdG9yczogc29tZSBmYWN0b3JzIGFyZSBhY3RpdmUgb25seSB3aGVuIHBob3NwaG9yeWxhdGVkLCBtZWFuaW5nIHRoYXQgdGhlaXIgdHJhbnNjcmlwdHMgbWF5IGJlIGNvbnN0aXR1dGl2ZWx5IGV4cHJlc3NlZCB3aGlsZSB0aGVpciBhY3Rpdml0eSBkZXBlbmRzIG9uIHBob3NwaG9yeWxhdGlvbiwgY29ycmVsYXRpbmcgaW5zdGVhZCB3aXRoIHRoZSBhY3Rpdml0eSBvZiB0aGUgY29ycmVzcG9uZGluZyBraW5hc2UuDQoNCldlIHdpbGwgYWxzbyBmb2N1cyBvbiBkZXZlbG9waW5nIGEgZ2VuZXJhbCBtb2RlbCB0byByZWxhdGUgdHJhbnNjcmlwdGlvbiBmYWN0b3IgYWN0aXZpdHkgdG8gdHJhbnNjcmlwdCBhYnVuZGFuY2UuIFNpZ25pZmljYW50IGRldmlhdGlvbnMgZnJvbSB0aGlzIG1vZGVsIGNvdWxkIGluZGljYXRlIHRyYW5zY3JpcHRpb24gZmFjdG9ycyB3aG9zZSBhY3Rpdml0eSBpcyBub3QgYWNjdXJhdGVseSByZWZsZWN0ZWQgYnkgdHJhbnNjcmlwdCBsZXZlbHMuIE9uZSBjaGFsbGVuZ2Ugb2YgdGhpcyBhcHByb2FjaCBpcyB0aGUgZmFjdCB0aGF0IGdlbmVzIG9mIHRoZSBzYW1lIG9wZXJvbiBtYXkgYmUgaGlnaGx5ICpjby1leHByZXNzZWQqIHRvZ2V0aGVyIHdoaWNoIGlzIGEgcHJvYmxlbSBmb3IgbWFueSBtb2RlbHMgbGlrZSAqKmxpbmVhciByZWdyZXNzaW9uKiosIHNvIHdlIHdpbGwgdHJ5IHRvIGFkZHJlc3MgaXQgZHVyaW5nIG91ciBhbmFseXNpcy4NCg0KIyMjIFRhYmxlIG9mIENvbnRlbnRzDQoNCkhlcmUgYXJlIHRoZSBzdGVwcyB0aGF0IHdlIHBlcmZvcm1lZCBpbiBvdXIgYW5hbHlzaXMNCg0KMS4gIFtJbnRyb2R1Y3Rpb25dDQoNCjIuICBbTGlicmFyeSBsb2FkaW5nXQ0KDQozLiAgW1ByZS1wcm9jZXNzaW5nXQ0KDQo0LiAgW0V4cGxvcmF0b3J5IEFuYWx5c2lzXQ0KDQo1LiAgW01vZGVsbGluZ10gLVtjQU1QIFJlY2VwdG9yIFByb3RlaW4gKGNycCldKCNjYW1wLXJlY2VwdG9yLXByb3RlaW4tY3JwKQ0KDQojIExpYnJhcnkgbG9hZGluZw0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KbGlicmFyeShyZWFkcikNCmxpYnJhcnkobWF0cml4U3RhdHMpDQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShncmlkKQ0KbGlicmFyeShncmlkRXh0cmEpDQpsaWJyYXJ5KGNhcmV0KQ0KbGlicmFyeShnbG1uZXQpDQpsaWJyYXJ5KGlncmFwaCkNCmxpYnJhcnkodmlwKQ0KbGlicmFyeShnZ3JlcGVsKQ0KYGBgDQoNCiMgUHJlLXByb2Nlc3NpbmcNCg0KTGV0J3Mgc3RhcnQgaW1wb3J0aW5nIHRoZSBkYXRhc2V0IGZyb20gW1ByZWNpc2VEQl0oaHR0cHM6Ly93d3cubmNiaS5ubG0ubmloLmdvdi9wbWMvYXJ0aWNsZXMvUE1DNTQwMDQ1Lykgd2hpY2ggaXMgYW4gUk5BLXNlcSBjb21wZW5kaXVtIGZvciAqRXNjaGVyaWNoaWEgY29saSogZnJvbSB0aGUgU3lzdGVtcyBCaW9sb2d5IFJlc2VhcmNoIGdyb3VwIGF0IFVDIFNhbiBEaWVnbyB3aGljaCBjb250YWlucyB0aGUgZXhwcmVzc2lvbiBsZXZlbHMgZm9yIDMsOTIzIGdlbmVzIGluIEUuIGNvbGkgZm9yIDI3OCBkaWZmZXJlbnQgY29uZGl0aW9uLiBUaGUgYXBwcm9hY2ggdGhleSB1c2VkIHdhcyBjdWx0dXJpbmcgYmFjdGVyaWEgb24gbWFueSBkaWZmZXJlbnQgbWVkaWEgYW5kIHRoZXkgbWVhc3VyZWQgZ2VuZSBleHByZXNzaW9uIHZhbHVlcyB0aGVuIHRoZXkgcHJvY2VlZGVkIHRvIGtub2NrLW91dCBtYW55IGRpZmZlcmVudCBnZW5lcyBhbmQgY3VsdHVyZWQgYWdhaW4gb24gZGlmZmVyZW50IG1lZGlhIHRvIG1lYXN1cmUgZXhwcmVzc2lvbiBhZ2FpbiBiZWNhdXNlIHRoZXkgd2VyZSBleHBlY3RpbmcgdmFyaWF0aW9ucyBvZiBleHByZXNzaW9uOyBmb3IgZXhhbXBsZSwgdGhleSBrbm9ja2VkIG91dCBhIGdlbmUgZm9yIGlyb24gbWV0YWJvbGlzbSBvbiBhIG1lZGl1bSBjb250YWluaW5nIGlyb24gYW5kIHRoaXMgZm9yIHN1cmUgYWZmZWN0cyB0aGUgZXhwcmVzc2lvbiBsZXZlbHMgb2YgdGhlICpFLmNvbGkqIGN1bHR1cmUgYmVjYXVzZSB0aGUgb3JnYW5pc20gaXMgbm90IGFibGUgdG8gbWFuYWdlIGlyb24gY29ycmVjdGx5IGFueW1vcmUuDQoNCmBgYHtyfQ0KIyBJbXBvcnQgZmlsZSANCmxvZ190cG0gPC0gcmVhZC5jc3YoImxvZ190cG1fZnVsbC5jc3YiLCByb3cubmFtZXMgPSAxKQ0KbG9nX3RwbQ0KYGBgDQoNCk5leHQgd2Ugd2FudCB0byBwZXJmb3JtIHNvbWUgcHJlLXByb2Nlc3NpbmcuIEZpcnN0IHdlIHdhbnQgdG8gZXhjbHVkZToNCg0KLSAgIENvbmRpdGlvbiB3aXRoIGtub2NrLW91dCBnZW5lcw0KDQotICAgR2VuZXMgd2l0aCBpc29mb3Jtcw0KDQpGb3IgdGhlIG5hdHVyZSBvZiBvdXIgYW5hbHlzaXMgd2UgYXJlIG5vdCBpbnRlcmVzdGVkIGluIHRoZSBjb25kaXRpb25zIGluIHdoaWNoIGEgZ2VuZSBpcyBrbm9ja2VkLW91dCBiZWNhdXNlIHRoZSBpbmZsdWVuY2Ugb2YgdGhlIGtub2NrLW91dCBnZW5lIG1heSBiZSBodWdlIG9uIGl0cyByZWxhdGVkIHBhdGh3YXlzLCBmb3IgdGhpcyByZWFzb24gd2UgZXhjbHVkZWQgdGhlbSBiZWNhdXNlIHdlIHdhbnQgdW5iaWFzZWQgcmVzdWx0cy4gUmVzZWFyY2hlcnMgb2YgUHJlY2lzZURCIGFsc28gY29sbGVjdGVkIGV4cHJlc3Npb24gZGF0YSBhYm91dCBtYW55IGdlbmUgaXNvZm9ybXMsIHRoZSBwcm9ibGVtIGlzIHRoYXQgdGhlIGNvbGxlY3Rpb24gb2YgaXNvZm9ybXMgd2FzIG5vdCBjb21wbGV0ZSBhbmQgdGhlcmUgd2FzIG5vdCBhIHJlbGlhYmxlIHdheSB0byBkZWNpZGUgd2hpY2ggaXNvZm9ybSB0byBjb25zaWRlcjogY29uc2lkZXJpbmcgYWxsIG9mIHRoZW0gZHVyaW5nIHRoZSBwcmVwcm9jZXNzaW5nIHdvdWxkIGhhdmUgcmVzdWx0ZWQgaW4gaGF2aW5nIHVubWFwcGFibGUgYm51bWJlcnMgYW5kIGR1cGxpY2F0ZSBnZW5lcyBzbyB3ZSByZW1vdmVkIHRoZW0gZnJvbSB0aGUgZGF0YXNldCBiZWZvcmUgcHJvY2VlZGluZy4NCg0KYGBge3J9DQojIEV4Y2x1ZGUgY29uZGl0aW9uIHdpdGgga25vY2tvdXQgZ2VuZXMNCmxvZ190cG0gPC0gbG9nX3RwbVssIC1ncmVwKCJfZGVsIiwgY29sbmFtZXMobG9nX3RwbSkpXQ0KDQojIERyb3AgZ2VuZXMgd2l0aCBpc29mb3JtDQpnZW5lc193aXRoX2lzb2Zvcm1zIDwtIGdyZXAoIl9cXGQrJCIsIHJvd25hbWVzKGxvZ190cG0pLCB2YWx1ZSA9IFRSVUUpDQoNCmxvZ190cG0gPC0gc3Vic2V0KGxvZ190cG0sICEocm93bmFtZXMobG9nX3RwbSkgJWluJSBnZW5lc193aXRoX2lzb2Zvcm1zKSkNCmRpbShsb2dfdHBtKQ0KYGBgDQoNCk5vdyB3ZSBjYW4gcHJvY2VlZCB0byBpbXBvcnQgdGhlIHJlZ3VsYXRvcnkgbmV0d29yayBmcm9tIFtSZWd1bG9uREJdKGh0dHBzOi8vcmVndWxvbmRiLmNjZy51bmFtLm14LykgdGhhdCByZXBvcnRzIHRoZSB0YXJnZXQgZ2VuZXMgZm9yIGVhY2ggcmVndWxhdG9yLiBFYWNoIHJvdyBvZiB0aGlzIGRhdGFzZXQgcmVwcmVzZW50cyBhbiBlZGdlIGluIGEgcmVndWxhdG9yeSBuZXR3b3JrIGdyYXBoIGFuZCBjb250YWlucyBpbmZvcm1hdGlvbnMgYWJvdXQgdGhlIGludGVyYWN0aW9uIG9mIHdoaWNoIHRoZSBtb3N0IGltcG9ydGFudCBvbmUgaXMgcHJvYmFibHkgaWYgdGhlIGludGVyYWN0aW9uIGlzIHBvc2l0aXZlIG9yIG5lZ2F0aXZlLg0KDQpgYGB7cn0NCnJlZ3VsYXRvciA8LSByZWFkLnRhYmxlKGZpbGU9InRhYmxlRGF0YVJlZy5jc3YiLA0KICAgICAgICAgICAgICAgICAgICAgICAgaGVhZGVyPVRSVUUsIA0KICAgICAgICAgICAgICAgICAgICAgICAgc2VwPSIsIikNCnJlZ3VsYXRvcg0KYGBgDQoNCldlIGRlY2lkZWQgdG8gZWxpbWluYXRlOg0KDQotICAgRW50cmllcyB0aGF0IGFyZSBub3QgcmVmZXJyZWQgdG8gYSBwcm90ZWluIHJlZ3VsYXRvci4NCg0KLSAgIFJlbGF0aW9uc2hpcHMgcmVwb3J0ZWQgYXMgKldlYWsqIG9yICpVbmtub3duKiB0byB3b3JrIG9ubHkgb24gbWVhbmluZ2Z1bCBpbnRlcmFjdGlvbnMuDQoNCmBgYHtyfQ0KcmVndWxhdG9yIDwtIHJlZ3VsYXRvclt3aGljaChyZWd1bGF0b3JbLCAyXSAhPSAicHBHcHAiKSwgXQ0KDQp3IDwtIHdoaWNoKHRyaW13cyhyZWd1bGF0b3JbLDddKT09IlciKQ0KaWYobGVuZ3RoKHcpPjApew0KICByZWd1bGF0b3IgPC0gcmVndWxhdG9yWy13LF0NCn0NCg0KdyA8LSB3aGljaCh0cmltd3MocmVndWxhdG9yWyw3XSk9PSI/IikNCmlmKGxlbmd0aCh3KT4wKXsNCiAgcmVndWxhdG9yIDwtIHJlZ3VsYXRvclstdyxdDQp9DQoNCm5yb3cocmVndWxhdG9yKQ0KYGBgDQoNClRoZXJlIGlzIGEgZGlzY3JlcGFuY3kgYmV0d2VlbiB0aGUgZ2VuZSBpZGVudGlmaWVycyBwcmVzZW50IGluIHRoZSByZWd1bGF0b3J5IG5ldHdvcmsgb2J0YWluZWQgZnJvbSBSZWd1bG9uREIgYW5kIHRob3NlIGluIHRoZSBQcmVjaXNlREIgZGF0YXNldC4gV2hpbGUgdGhlIHJlZ3VsYXRvcnkgbmV0d29yayB1c2VzIGdlbmUgbmFtZXMsIG91ciBkYXRhc2V0IGNvbnRhaW5lZCBpZGVudGlmaWVycyBpbiB0aGUgZm9ybSBvZiBibnVtYmVycy4gU28gaXQncyBiZXR0ZXIgdG8gY29udmVydCBibnVtYmVycyB0byBnZW5lIG5hbWVzIHRvIGZhY2lsaXRhdGUgY29tcGFyaXNvbiBhbmQgZG93bnN0cmVhbSBhbmFseXNpcy4gSW4gYWRkaXRpb24gd2UgZGVjaWRlZCB0bzoNCg0KLSAgIFJlbW92ZSBlbnRyaWVzIHdpdGggYm51bWJlcnMgdGhhdCBkb24ndCBtYXAgd2l0aCBhIGdlbmUgbmFtZQ0KDQotICAgUmVtb3ZlIGR1cGxpY2F0ZSBnZW5lcw0KDQpgYGB7cn0NCiMgTG9hZGluZyB0aGUgZmlsZSBmcm9tIEVDT2N5YyB3aGljaCBjb250YWluIHRoZSBtYXBwaW5nIGJudW0tZ2VuZW5hbWUNCm1hcF9ibnVtIDwtIHJlYWQuZGVsaW0oIm1hcGJudW0udHh0IiwgaGVhZGVyID0gVFJVRSkNCg0KIyBLZWVwIG9ubHkgdGhlIHVzZWZ1bCBjb2x1bW5zDQptYXBfYm51bSA8LSBtYXBfYm51bVtjKCJHZW5lLk5hbWUiLCAiQWNjZXNzaW9uLjEiKV0NCg0KIyBNYXAgYmV0d2VlbiBvdXIgZGF0YXNldCBhbmQgdGhlIGZpbGUgb2YgZWNvY3ljDQpsb2dfdHBtJGdlbmVfbnVtYmVyIDwtIHJvd25hbWVzKGxvZ190cG0pDQpsb2dfdHBtIDwtIG1lcmdlKGxvZ190cG0sIG1hcF9ibnVtLCBieS54ID0gImdlbmVfbnVtYmVyIiwgYnkueSA9ICJBY2Nlc3Npb24uMSIsIGFsbC54ID0gVFJVRSkNCg0KIyBSZWFycmFuZ2UgdGhlIGRhdGFzZXQNCmxvZ190cG0gPC0gbG9nX3RwbVssIGMoIkdlbmUuTmFtZSIsIHNldGRpZmYobmFtZXMobG9nX3RwbSksICJHZW5lLk5hbWUiKSldDQoNCiMgUmVtb3ZpbmcgdW5tYXBwZWQgZ2VuZXMgYm51bWJlcg0KbG9nX3RwbSA8LSBzdWJzZXQobG9nX3RwbSwgIWlzLm5hKEdlbmUuTmFtZSkpDQoNCiMgUmVtb3ZpbmcgZHVwbGljYXRlIGdlbmVzIChpdCBhbHNvIGhhcyBhbGwgZXhwcmVzc2lvbiB2YWx1ZXMgZXF1YWwgMCBzbyB2ZXJ5IGJhZCkNCmxvZ190cG0gPC0gc3Vic2V0KGxvZ190cG0sICEobG9nX3RwbSRHZW5lLk5hbWUgPT0gImluc0kyIikpDQoNCiMgU2V0dGluZyByb3duYW1lcyBhbmQgZHJvcHBpbmcgdGhlIGZpcnN0IDIgY29sdW1ucw0Kcm93bmFtZXMobG9nX3RwbSkgPC0gbG9nX3RwbSRHZW5lLk5hbWUNCmxvZ190cG0gPC0gbG9nX3RwbVssMzpuY29sKGxvZ190cG0pXQ0KDQojIFRyYW5zcG9zZSBsb2dfdHBtIGluIG9yZGVyIHRvIGhhdmUgZ2VuZXMgYXMgY29sdW1ucyBhbmQgY29uZGl0aW9ucyBhcyByb3dzDQpsb2dfdHBtIDwtIHQobG9nX3RwbSkNCmBgYA0KDQojIEV4cGxvcmF0b3J5IEFuYWx5c2lzDQoNCldlIHdvdWxkIGxpa2UgdG8gdmVyaWZ5IGlmIHdlIGNhbiB1c2Ugb25lIHRoZSBmb3VyIG1ham9yIHN1bW1hcnkgc3RhdGlzdGljcyB3aGljaCBhcmUgKiptZWFuKiosICoqbWVkaWFuKiosICoqbWF4aW11bSoqIGFuZCAqKm1pbmltdW0qKiBvZiBleHByZXNzaW9uIG9mIGVhY2ggZ2VuZSB0byBtb2RlbCB0aGUgKnJlbGF0aW9uc2hpcCBiZXR3ZWVuIHRoZSB0YXJnZXRzIGFuZCB0aGVpciByZWd1bGF0b3IqLiBOb3cgbGV0J3MgYW5hbHl6ZSB0aGUgZGlzdHJpYnV0aW9uIG9mIHRoZXNlIHN0YXRpc3RpY3MgdG8gdW5kZXJzdGFuZCB3aGljaCBpcyB0aGUgYmVzdCB2YWx1ZSB0byBjaG9vc2UsIGlmIGFueSwgdGhlbiB3ZSBhbHNvIHdhbnQgY2hlY2sgaWYgdGhleSBmb2xsb3cgYSAqKkdhdXNzaWFuIGRpc3RyaWJ1dGlvbioqIHVzaW5nIHRoZSBTaGFwaXJvLS1XaWxrIHRlc3Qgd2hpY2ggY29tcHV0ZXMgdGhlIFcgc3RhdGlzdGljcyBhcyBmb2xsb3dzOg0KDQokJA0KVyA9IFxkZnJhY3tcYmlnKFxzdW1ebiBfIHtpPTF9IGFfaSB4X3soaSl9IFxiaWcgKSBeMn17XHN1bV5uIF8ge2k9MX0gKHhfaSAtIFxiYXJ7eH0pXjJ9DQokJA0KDQpUaGVuIHRoaXMgVyB2YWx1ZSBpcyB1c2VkIGZvciB0aGUgaHlwb3RoZXNpcyB0ZXN0aW5nIHRoYXQgZm9sbG93czoNCg0KJCQNCkhfbzogXHRleHR7YSBzYW1wbGUgfSB4XzEsIFxjZG90cywgeF9uIFx0ZXh0eyBpcyBkcmF3biBmcm9tIGEgbm9ybWFsbHkgZGlzdHJpYnV0ZWQgcG9wdWxhdGlvbi59IFxcDQpIX2E6IFx0ZXh0e2Egc2FtcGxlIH0geF8xLCBcY2RvdHMsIHhfbiBcdGV4dHsgaXMgbm90IGRyYXduIGZyb20gYSBub3JtYWxseSBkaXN0cmlidXRlZCBwb3B1bGF0aW9uLn0NCiQkDQoNCklmIHRoZSB0ZXN0IHJldHVybnMgYSAqcC12YWx1ZSogbG93ZXIgdGhlbiB0aGUgdGhyZXNob2xkIDAuMDUgaXQgbWVhbnMgdGhhdCB0aGUgZGF0YSAqKmRvIG5vdCoqIGZvbGxvdyBhIG5vcm1hbCBkaXN0cmlidXRpb24uDQoNCmBgYHtyfQ0KIyBIaXN0b2dyYW1zIG9mIFN1bW1hcnkgU3RhdGlzdGljcw0KDQojIE1FQU4NCmxvZ190cG1fbWVhbiA8LSBkYXRhLmZyYW1lKHZhbHVlID0gYXBwbHkobG9nX3RwbSwgMiwgbWVhbikpDQptZWFuX2hpc3QgPC0gZ2dwbG90KGxvZ190cG1fbWVhbiwgYWVzKHggPSB2YWx1ZSkpICsNCiAgICAgICAgICAgICAgICAgICAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAwLjUsIGZpbGwgPSAic2t5Ymx1ZSIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9ICJibGFjayIsIGJpbnMgPSAxMDApICsNCiAgICAgICAgICAgICAgICAgICAgbGFicyh4ID0gIk1lYW4gbG9nLVRQTSIsIHkgPSAiRnJlcXVlbmN5IikgKw0KICAgICAgICAgICAgICAgICAgICB0aGVtZV9taW5pbWFsKCkgKw0KICAgICAgICAgICAgICAgICAgICB0aGVtZShwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSkNCg0KIyBNRURJQU4NCmxvZ190cG1fbWVkaWFuIDwtIGRhdGEuZnJhbWUodmFsdWUgPSBhcHBseShsb2dfdHBtLCAyLCBtZWRpYW4pKQ0KbWVkaWFuX2hpc3QgPC0gZ2dwbG90KGxvZ190cG1fbWVkaWFuLCBhZXMoeCA9IHZhbHVlKSkgKw0KICAgICAgICAgICAgICAgICAgICAgIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMC41LCBmaWxsID0gImxpZ2h0Z3JlZW4iLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9ICJibGFjayIsIGJpbnMgPSAxMDApICsNCiAgICAgICAgICAgICAgICAgICAgICBsYWJzKHggPSAiTWVkaWFuIGxvZy1UUE0iLCB5ID0gIkZyZXF1ZW5jeSIpICsNCiAgICAgICAgICAgICAgICAgICAgICB0aGVtZV9taW5pbWFsKCkgKw0KICAgICAgICAgICAgICAgICAgICAgIHRoZW1lKHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCkpDQoNCiMgTUFYDQpsb2dfdHBtX21heCA8LSBkYXRhLmZyYW1lKHZhbHVlID0gYXBwbHkobG9nX3RwbSwgMiwgbWF4KSkNCm1heF9oaXN0IDwtIGdncGxvdChsb2dfdHBtX21heCwgYWVzKHggPSB2YWx1ZSkpICsNCiAgICAgICAgICAgICAgICAgICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDAuNSwgZmlsbCA9ICJsYXZlbmRlciIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gImJsYWNrIiwgYmlucyA9IDEwMCkgKw0KICAgICAgICAgICAgICAgICAgIGxhYnMoeCA9ICJNYXggbG9nLVRQTSIsIHkgPSAiRnJlcXVlbmN5IikgKw0KICAgICAgICAgICAgICAgICAgIHRoZW1lX21pbmltYWwoKSArDQogICAgICAgICAgICAgICAgICAgdGhlbWUocGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSkNCg0KIyBNSU4NCmxvZ190cG1fbWluIDwtIGRhdGEuZnJhbWUodmFsdWUgPSBhcHBseShsb2dfdHBtLCAyLCBtaW4pKQ0KbWluX2hpc3QgPC0gZ2dwbG90KGxvZ190cG1fbWluLCBhZXMoeCA9IHZhbHVlKSkgKw0KICAgICAgICAgICAgICAgICAgIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMC41LCBmaWxsID0gImxpZ2h0cGluayIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gImJsYWNrIiwgYmlucyA9IDEwMCkgKw0KICAgICAgICAgICAgICAgICAgIGxhYnMoeCA9ICJNaW4gbG9nLVRQTSIsIHkgPSAiRnJlcXVlbmN5IikgKw0KICAgICAgICAgICAgICAgICAgIHRoZW1lX21pbmltYWwoKSArDQogICAgICAgICAgICAgICAgICAgdGhlbWUocGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSkNCg0KIyBGaW5hbCBQbG90DQpncmlkLmFycmFuZ2UobWVhbl9oaXN0LCBtZWRpYW5faGlzdCwgbWF4X2hpc3QsIG1pbl9oaXN0LCBucm93ID0gMiwgbmNvbCA9IDIsDQogICAgICAgICAgICAgdG9wID0gdGV4dEdyb2IoIkhpc3RvZ3JhbXMgb2YgU3VtbWFyeSBTdGF0aXN0aWNzIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3A9Z3Bhcihmb250c2l6ZT0xNikpKQ0KYGBgDQoNCmBgYHtyfQ0KI01FQU4NCnNoYXBpcm8udGVzdChsb2dfdHBtX21lYW4kdmFsdWUpICNub3QgZ2F1c3NpYW4NCg0KI01FRElBTg0Kc2hhcGlyby50ZXN0KGxvZ190cG1fbWVkaWFuJHZhbHVlKSAjbm90IGdhdXNzaWFuDQoNCiNNQVhJTVVNDQpzaGFwaXJvLnRlc3QobG9nX3RwbV9tYXgkdmFsdWUpICNub3QgZ2F1c3NpYW4NCg0KI01JTklNVU0NCnNoYXBpcm8udGVzdChsb2dfdHBtX21pbiR2YWx1ZSkgI25vdCBnYXVzc2lhbg0KYGBgDQoNCkJhc2VkIG9uIHRoZSByZXN1bHRzIG9mIHRoZSBTaGFwaXJvLVdpbGsgdGVzdHMgY29uZHVjdGVkIG9uIHRoZSBmb3VyIGRpc3RyaWJ1dGlvbnMgYW5kIHRoZWlyIHJlc3BlY3RpdmUgaGlzdG9ncmFtcywgd2UgY2FuIGNvbmNsdWRlIHRoYXQgdGhlIGRpc3RyaWJ1dGlvbnMgZG8gbm90IGZvbGxvdyB0byBhIEdhdXNzaWFuIChub3JtYWwpIGRpc3RyaWJ1dGlvbi5cDQpDb25zZXF1ZW50bHksIHRoaXMgaW1wbGllcyB0aGF0IGNob29zaW5nIG1lYXN1cmVzIHN1Y2ggYXMgdGhlIG1lYW4sIG1lZGlhbiwgbWF4aW11bSwgb3IgbWluaW11bSBtYXkgbm90IG1hdGNoIHRoZSBsaW5lYXIgcmVncmVzc2lvbiBhc3N1bXB0aW9uIHRoYXQgdGhlIGRhdGEgZm9sbG93cyBhIEdhdXNzaWFuIChub3JtYWwpIGRpc3RyaWJ1dGlvbiwgZm9yIHRoaXMgcmVhc29uIGl0IGlzIG5vdCBwb3NzaWJsZSB0byB1c2Ugb25lIG9mIHRoZXNlIHN0YXRpc3RpY3MgYXMgYSBwcmVkaWN0b3IgaW4gb3VyIG1vZGVsIGJlY2F1c2Ugbm9uZSBvZiB0aGVtIGlzIG5vcm1hbGx5IGRpc3RyaWJ1dGVkIGJ1dCB0aGUgYmlnZ2VzdCBwcm9ibGVtIGlzIHRoYXQgd2Ugd291bGQgdHJhaW4gYSBtb2RlbCBvbiBhIHNpbmdsZSBzYW1wbGUgYW5kIGl0IGRvZXMgbm90IG1ha2UgYW55IHNlbnNlLiBJbiB0aGUgbmV4dCBzZWN0aW9uIGFib3V0IG1vZGVsbGluZyB3ZSB3aWxsIHRyeSBpbnN0ZWFkIHRvIHRha2UgdGhlIG1lYW4gb2YgYWxsIHRoZSB0YXJnZXRzIGluIGV2ZXJ5IGNvbmRpdGlvbiBhbmQgZml0IGEgbW9kZWwgd2l0aCAxIGZlYXR1cmUgKG1lYW4gb2YgdGhlIHRhcmdldHMpIGFuZCBhcyBtYW55IHNhbXBsZXMgYXMgdGhlIGNvbmRpdGlvbnMgdGhhdCB3ZSBoYXZlLg0KDQpOb3cgd2UgcGxvdCBzb21lIGhpc3RvZ3JhbXMgdG8gaGF2ZSBhIGJldHRlciBpZGVhIG9uIHRoZSBudW1iZXIgb2YgcG9zaXRpdmUsIG5lZ2F0aXZlIGFuZCB0b3RhbCBpbnRlcmFjdGlvbnMgb2YgZWFjaCByZWd1bGF0b3IgYW5kIGFnYWluIHdlIHBlcmZvcm0gYSBTaGFwaXJvLS1XaWxrIHRlc3QgZm9yIGVhY2ggb25lIG9mIHRoZW0gdG8gY2hlY2sgZm9yIG5vcm1hbGl0eS4NCg0KYGBge3J9DQojIERpdmlkZSBwb3NpdGl2ZSBhbmQgbmVnYXRpdmUgcmVndWxhdGlvbg0KcG9zaXRpdmVfcmVnIDwtIHJlZ3VsYXRvcltyZWd1bGF0b3IkWDYuZnVuY3Rpb24gPT0gIisiLF0NCm5lZ2F0aXZlX3JlZyA8LSByZWd1bGF0b3JbcmVndWxhdG9yJFg2LmZ1bmN0aW9uID09ICItIixdDQoNCiMgSWRlbnRpZnkgdGhlIGRpZmZlcmVudCByZWd1bGF0b3JzIGluIHRoZSBkYXRhc2V0DQp1bmlxdWVfcmVndWxhdG9ycyA8LSB1bmlxdWUocmVndWxhdG9yJFgzLlJlZ3VsYXRvckdlbmVOYW1lKQ0KDQpwb3NfY291bnRzIDwtIGMoKQ0KbmVnX2NvdW50cyA8LSBjKCkNCg0KIyBDb3VudCBob3cgbWFueSBnZW5lcyBhcmUgcmVndWxhdGVkIGJ5IGVhY2ggcmVndWxhdG9yDQpmb3IocmVnIGluIHVuaXF1ZV9yZWd1bGF0b3JzKXsNCiAgcG9zX2NvdW50cyA8LSBjKHBvc19jb3VudHMsIA0KICAgICAgICAgICAgICAgICAgY291bnQocG9zaXRpdmVfcmVnW3Bvc2l0aXZlX3JlZyRYMy5SZWd1bGF0b3JHZW5lTmFtZSA9PSByZWcsXSkpDQogIG5lZ19jb3VudHMgPC0gYyhuZWdfY291bnRzLCANCiAgICAgICAgICAgICAgICAgIGNvdW50KG5lZ2F0aXZlX3JlZ1tuZWdhdGl2ZV9yZWckWDMuUmVndWxhdG9yR2VuZU5hbWUgPT0gcmVnLF0pKQ0KfQ0KDQpwb3NfY291bnRzIDwtIHVubGlzdChwb3NfY291bnRzKQ0KbmFtZXMocG9zX2NvdW50cykgPC0gdW5pcXVlX3JlZ3VsYXRvcnMNCg0KbmVnX2NvdW50cyA8LSB1bmxpc3QobmVnX2NvdW50cykNCm5hbWVzKG5lZ19jb3VudHMpIDwtIHVuaXF1ZV9yZWd1bGF0b3JzDQoNCnBvc19jb3VudHMgPC0gZGF0YS5mcmFtZSh2YWx1ZSA9IHBvc19jb3VudHMpDQpuZWdfY291bnRzIDwtIGRhdGEuZnJhbWUodmFsdWUgPSBuZWdfY291bnRzKQ0KdG90YWxfY291bnRzIDwtIGRhdGEuZnJhbWUodmFsdWUgPSBwb3NfY291bnRzJHZhbHVlICsgbmVnX2NvdW50cyR2YWx1ZSkNCg0KIyBIaXN0b2dyYW1zIG9mIGhvdyBtYW55IGdlbmVzIGFyZSByZWd1bGF0ZWQgYnkgZWFjaCBnZW5lDQoNCiMgUG9zaXRpdmUgQ291bnRzIEhpc3RvZ3JhbQ0KcG9zX2NvdW50c19oaXN0IDwtIGdncGxvdChwb3NfY291bnRzLCBhZXMoeCA9IHZhbHVlKSkgKw0KICAgICAgICAgICAgICAgICAgICAgICAgICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDEwLCBmaWxsID0gInNreWJsdWUiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSAiYmxhY2siLCBiaW5zID0gMjApICsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgbGFicyh4ID0gIlBvc2l0aXZlIFJlZ3VsYXRpb25zIENvdW50IiwgeSA9ICJGcmVxdWVuY3kiKSArDQogICAgICAgICAgICAgICAgICAgICAgICAgIHRoZW1lX21pbmltYWwoKSArDQogICAgICAgICAgICAgICAgICAgICAgICAgIHRoZW1lKHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpKQ0KDQojIE5lZ2F0aXZlIENvdW50cyBIaXN0b2dyYW0NCm5lZ19jb3VudHNfaGlzdCA8LSBnZ3Bsb3QobmVnX2NvdW50cywgYWVzKHggPSB2YWx1ZSkpICsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAxMCwgZmlsbCA9ICJsaWdodGdyZWVuIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gImJsYWNrIiwgYmlucyA9IDIwKSArDQogICAgICAgICAgICAgICAgICAgICAgICAgIGxhYnMoeCA9ICJOZWdhdGl2ZSBSZWd1bGF0aW9ucyBDb3VudCIsIHkgPSAiRnJlcXVlbmN5IikgKw0KICAgICAgICAgICAgICAgICAgICAgICAgICB0aGVtZV9taW5pbWFsKCkgKw0KICAgICAgICAgICAgICAgICAgICAgICAgICB0aGVtZShwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSkNCg0KIyBUb3RhbCBDb3VudHMgSGlzdG9ncmFtDQp0b3RhbF9jb3VudHNfaGlzdCA8LSBnZ3Bsb3QodG90YWxfY291bnRzLCBhZXMoeCA9IHZhbHVlKSkgKw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMTAsIGZpbGwgPSAibGlnaHRwaW5rIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSAiYmxhY2siLCBiaW5zID0gMjApICsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJzKHggPSAiVG90YWwgUmVndWxhdGlvbnMgQ291bnQiLCB5ID0gIkZyZXF1ZW5jeSIpICsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aGVtZV9taW5pbWFsKCkgKw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRoZW1lKHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCkpDQoNCiMgRmluYWwgUGxvdA0KZ3JpZC5hcnJhbmdlKHBvc19jb3VudHNfaGlzdCwgbmVnX2NvdW50c19oaXN0LCB0b3RhbF9jb3VudHNfaGlzdCwgbmNvbCA9IDEpDQpgYGANCg0KYGBge3J9DQojIFBvc2l0aXZlIEludGVyYWN0aW9ucyBDb3VudHMgbm9ybWFsaXR5IHRlc3QNCnNoYXBpcm8udGVzdChwb3NfY291bnRzJHZhbHVlKSAjbm90IGdhdXNzaWFuDQoNCiMgTmVnYXRpdmUgSW50ZXJhY3Rpb25zIENvdW50cyBub3JtYWxpdHkgdGVzdA0Kc2hhcGlyby50ZXN0KG5lZ19jb3VudHMkdmFsdWUpICNub3QgZ2F1c3NpYW4NCg0KIyBUb3RhbCBJbnRlcmFjdGlvbnMgQ291bnRzIG5vcm1hbGl0eSB0ZXN0DQpzaGFwaXJvLnRlc3QodG90YWxfY291bnRzJHZhbHVlKSAjbm90IGdhdXNzaWFuDQpgYGANCg0KVGhlc2UgaGlzdG9ncmFtcyBzaG93IGhvdyBtYW55IGdlbmVzIGFyZSByZWd1bGF0ZWQgYnkgaG93IG1hbnkgcmVndWxhdG9ycywgdGhlIGhvcml6b250YWwgYXhpcyBpbmRpY2F0ZXMgaG93IG1hbnkgZ2VuZXMgYXJlIGNvbnRyb2xsZWQgYnkgdGhlIHJlZ3VsYXRvciBhbmQgdGhlIHZlcnRpY2FsIGF4aXMgaW5kaWNhdGVzIGhvdyBtYW55IHJlZ3VsYXRvcnMgaGF2ZSB0aGF0IG51bWJlciBvZiBpbnRlcmFjdGlvbnM7IGZvciBleGFtcGxlIHRoZXJlIGFyZSBtb3JlIHRoYW4gMTUwIHJlZ3VsYXRvcnMgdGhhdCBwb3NpdGl2ZWx5IGludGVyYWN0IHdpdGggMCBnZW5lcywgdGhpcyBpcyBub3Qgc3VycHJpc2luZyBiZWNhdXNlIHdlIHJlbW92ZWQgd2VhayBhbmQgdW5rbm93biBpbnRlcmFjdGlvbnMgYW5kIHdlIGNhbiBhbHNvIHNlZSB0aGF0IHRoZXJlIGlzIGEgZ2VuZSB0aGF0IG5lZ2F0aXZlbHkgY29udHJvbHMgYSBudW1iZXIgb2YgZ2VuZXMgY2xvc2UgdG8gNDAwLiBUaGVyZSBhcmUgc2xpZ2h0bHkgbW9yZSBuZWdhdGl2ZSByZWd1bGF0b3JzIHRoYXQgaW50ZXJhY3Qgd2l0aCBhdCBsZWFzdCBvbmUgZ2VuZSB0aGVuIHBvc2l0aXZlLiBJbiBhZGRpdGlvbiB3ZSBvYnNlcnZlIHRoYXQgbWFqb3JpdHkgb2YgZ2VuZXMgZG9lcyBub3QgaW50ZXJhY3Qgd2l0aCBvdGhlciBnZW5lcyBidXQgdGhlcmUgYXJlIHNvbWUgZXhjZXB0aW9ucyBsaWtlIHRoZSBnZW5lICoqY3JwKiogd2hpY2ggcG9zaXRpdmVseSBpbnRlcmFjdHMgd2l0aCAzMTAgb3RoZXIgZ2VuZXMuIFdlIGNhbiBhbHNvIG5vdGUgdGhhdCBub25lIG9mIHRoZXNlIGlzIG5vcm1hbGx5IGRpc3RyaWJ1dGVkLg0KDQojIyMgTmV0d29yayBhbmFseXNpcw0KDQpgYGB7cn0NCiMgQ3JlYXRpbmcgYWRqIG1hdHJpeCB0byBwbG90IHRoZSBuZXR3b3JrDQplZGdlX2xpc3QgPC0gY2JpbmQoUmVndWxhdG9yTmFtZSA9IHJlZ3VsYXRvciRYMy5SZWd1bGF0b3JHZW5lTmFtZSwgDQogICAgICAgICAgICAgICAgICAgVGFyZ2V0ID0gcmVndWxhdG9yJFg1LnJlZ3VsYXRlZE5hbWUpDQpncmFwaCA8LSBncmFwaF9mcm9tX2VkZ2VsaXN0KGVkZ2VfbGlzdCkNCmxheW91dCA8LSBsYXlvdXRfd2l0aF9mcihncmFwaCwgbml0ZXI9MTAwKQ0KDQoNCiMgVmlzdWFsaXp6YXppb25lIDNEIGRlbGxhIG5ldHdvcmsNCnBsb3QuaWdyYXBoKGdyYXBoLCBsYXlvdXQ9bGF5b3V0LCB2ZXJ0ZXgubGFiZWw9TkEsIHZlcnRleC5zaXplPTMsIGVkZ2UuYXJyb3cuc2l6ZT0wLjIsIGVkZ2UuY3VydmVkPVRSVUUsIG1haW49IkUuY29saSBOZXR3b3JrIiwgeGxpbT1jKC0wLjUsIDAuNSksIHlsaW09YygtMSwgMSkpDQoNCmBgYA0KDQojIE1vZGVsbGluZw0KDQpJbiBvcmRlciB0byBmaW5kIGEgbWF0aGVtYXRpY2FsIG1vZGVsIGFibGUgdG8gZGVzY3JpYmUgdGhlIGNvbXBsZXhpdHkgb2YgdGhpcyBiaW9sb2dpY2FsIHByb2JsZW0gd2Ugd291bGQgbGlrZSB0byBzdGFydCB3aXRoIHNpbXBsZXIgY2FzZXMgYW5kIGxvb2sgZm9yIHdoYXQgbWlnaHQgYmUgdGhlIGJlc3QgYXBwcm9hY2ggZm9yIHRoZW0gdGhlbiB3ZSB3aWxsIGFwcGx5IHRoaXMgYXBwcm9hY2ggdG8gZWFjaCByZWd1bGF0b3IuIEZvciB0aGlzIHJlYXNvbiB3ZSB3aWxsIGluZGl2aWR1YWxseSBwZXJmb3JtIHRoZSBtb2RlbGxpbmcgb24gdGhlIHBvc2l0aXZlIGludGVyYWN0aW9ucyBvZiAyIGdlbmVzIHRvIHNlZSB3aGljaCBtb2RlbCBjYW4gYmUgc3VpdGFibGUgdG8gYmUgYXBwbGllZCB0byBhbGwgdGhlIGdlbmVzOyBXZSBjaG9zZSB0aGUgZ2VuZXMgKipjcnAqKiBhbmQgKipnZW5lIGRhIHNjZWdsaWVyZSoqDQoNCiMjIGNBTVAgUmVjZXB0b3IgUHJvdGVpbiAoY3JwKSB7I2NhbXAtcmVjZXB0b3ItcHJvdGVpbi1jcnB9DQoNClRoZSBwcm90ZWluIENSUCBpcyBhICoqZ2xvYmFsIHRyYW5zY3JpcHRpb24gcmVndWxhdG9yKiosIHdoaWNoIHBsYXlzIGEgbWFqb3Igcm9sZSBpbiBjYXJib24gY2F0YWJvbGl0ZSByZXByZXNzaW9uIChDQ1IpIGFzIHdlbGwgYXMgb3RoZXIgcHJvY2Vzc2VzIGJlY2F1c2UgaXQgY2FuIGFjdCBhcyBhbiBhY3RpdmF0b3IsIHJlcHJlc3NvciwgY29hY3RpdmF0b3Igb3IgY29yZXByZXNzb3IuIENSUCBiaW5kcyBjeWNsaWMgQU1QIChjQU1QKSB3aGljaCBhbGxvc3RlcmljYWxseSBhY3RpdmF0ZXMgaXQsIHRoaXMgbWVhbnMgdGhhdCBpdHMgYWN0aXZpdHkgc2hvdWxkIG5vdCBiZSBpbmZsdWVuY2VkIGJ5IHRoZSBhY3Rpdml0eSBvZiBhbnkga2luYXNlLCB0aGVuIGl0IGJpbmRzIHRvIGl0cyBETkEgYmluZGluZyBzaXRlIHRvIGRpcmVjdGx5IHJlZ3VsYXRlIHRoZSB0cmFuc2NyaXB0aW9uIG9mIGFib3V0IDMwMCBnZW5lcyBpbiBhYm91dCAyMDAgb3Blcm9ucyBhbmQgaW5kaXJlY3RseSByZWd1bGF0ZSB0aGUgZXhwcmVzc2lvbiBvZiBhYm91dCBoYWxmIHRoZSBnZW5vbWUuDQoNCiMjIyBTaW1wbGUgTGluZWFyIFJlZ3Jlc3Npb24NCg0KVGhlIGZpcnN0IHRoaW5nIHdlIHdhbnQgdG8gdHJ5LCBhcyBhbnRpY2lwYXRlZCBpbiB0aGUgcHJldmlvdXMgc2VjdGlvbiwgaXMgdGFraW5nIHRoZSBtZWFuIG9mIGFsbCB0aGUgdGFyZ2V0cyBpbiBldmVyeSBjb25kaXRpb24gYW5kIGZpdCBhIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsIHdpdGggb25seSB0aGlzIG9uZSBmZWF0dXJlLiBMZXQncyBzdGFydCB0byB0YWtlIHRoZSBkYXRhIGFib3V0IGNycCBleHByZXNzaW9uIGFuZCB0aGUgZXhwcmVzc2lvbiBvZiBpdHMgdGFyZ2V0cw0KDQpgYGB7cn0NCiMgRXhwcmVzc2lvbiBvZiBjcnANCmNycF9leHAgPC0gbG9nX3RwbVssY29sbmFtZXMobG9nX3RwbSkgPT0gImNycCJdDQoNCiMgTGlzdCBvZiBwb3NpdGl2ZWx5IHJlZ3VsYXRlZCB0YXJnZXRzIGJ5IGNycA0KY3JwX3RhcmdldCA8LSBwb3NpdGl2ZV9yZWdbcG9zaXRpdmVfcmVnJFgzLlJlZ3VsYXRvckdlbmVOYW1lID09ICJjcnAiLF0NCg0KIyBFeHByZXNzaW9uIG9mIHRoZSB0YXJnZXRzDQpjcnBfdGFyZ2V0X2V4cCA8LSBsb2dfdHBtWyxjb2xuYW1lcyhsb2dfdHBtKSAlaW4lIGNycF90YXJnZXQkWDUucmVndWxhdGVkTmFtZV0NCmBgYA0KDQpOb3cgd2UgdGFrZSB0aGUgc2ltcGxlIG1lYW4gb2YgYWxsIHRoZSBnZW5lcyBvZiBlYWNoIGNvbmRpdGlvbiwgaW4gdGhpcyB3YXkgd2Ugd2lsbCBvYnRhaW4gYXMgbWFueSBtZWFucyBhcyB0aGUgbnVtYmVyIG9mIHNhbXBsZXMgKDE4OCkgYW5kIHdlIGZpdCBhIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsIHVzaW5nIHRoaXMgbWVhbiBhcyB0aGUgc29sZSBmZWF0dXJlLiBUaGVuIHdlIHB1dCB0b2dldGhlciBhIGRhdGFmcmFtZSB3aXRoIGFsbCB0aGUgZGF0YSBuZWVkZWQuDQoNCmBgYHtyfQ0KIyBBcHBseSB0aGUgbWVhbiB0byBlYWNoIHJvdyBvZiB0aGUgZGF0YXNldCAobWVhbiBvZiBlYWNoIGNvbmRpdGlvbikNCmNycF90YXJnZXRfbWVhbl9jb25kaXRpb25zIDwtIGFwcGx5KGNycF90YXJnZXRfZXhwLCAxLCBtZWFuKQ0KDQojIENyZWF0aW5nIHRoZSB0cmFpbmluZyBkYXRhZnJhbWUNCmNycF9tZWFuX3RyYWluIDwtIGRhdGEuZnJhbWUoY3JwID0gdW5saXN0KGNycF9leHApLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGFyZ2V0cyA9IHVubGlzdChjcnBfdGFyZ2V0X21lYW5fY29uZGl0aW9ucykpDQpgYGANCg0KQWxsIHRoZSBtb2RlbHMgdGhhdCB3ZSB3aWxsIGZpdCBpbiB0aGlzIHByb2plY3Qgd2lsbCBiZSB2YWxpZGF0ZWQgd2l0aCAqKjEwLWZvbGQgY3Jvc3MtdmFsaWRhdGlvbioqLCBjcm9zcy12YWxpZGF0aW9uIGlzIGEgc2ltcGxlIGFuZCB3aWRlbHkgdXNlZCBzYW1wbGluZyB0ZWNobmlxdWUgZm9yIGVzdGltYXRpbmcgcHJlZGljdGlvbiBlcnJvciB0byBhc3Nlc3MgaG93IHRoZSBtb2RlbCB3b3VsZCBhY3QgYWdhaW5zdCB1bnNlZW4gZGF0YS4gSXQgaXMgb2Z0ZW4gZG9uZSB3aGVuIHRoZXJlIGlzIG5vdCBlbm91Z2ggZGF0YSB0byBzZXQgYXNpZGUgYSB2YWxpZGF0aW9uIHNldCBhbmQgaXQgaGFzIG1hbnkgYWR2YW50YWdlcyBsaWtlIHByZXZlbnRpbmcgb3ZlcmZpdHRpbmcgYnkgcHJvdmlkaW5nIGEgbW9yZSByb2J1c3QgZXN0aW1hdGUgb2YgdGhlIG1vZGVsJ3MgcGVyZm9ybWFuY2VzIGFuZCBhbGxvd2luZyBtb2RlbCBzZWxlY3Rpb24gYW5kIGh5cGVycGFyYW1ldGVyIHR1bmluZyB3aGlsZSBiZWluZyBhdCB0aGUgc2FtZSB0aW1lIGRhdGEgZWZmaWNpZW50IGJlY2F1c2UgYWxsIHRoZSBkYXRhIGlzIHVzZWQgZm9yIHRyYWluaW5nIGFuZCB0ZXN0aW5nIHRoZSBtb2RlbC4gSW4gdGhlIGNhc2Ugb2YgbGluZWFyIHJlZ3Jlc3Npb24gdGhlIHB1cnBvc2Ugb2YgY3Jvc3MtdmFsaWRhdGlvbiBpcyBvbmx5IHRvIGdldCBhIHJvYnVzdCBlc3RpbWF0ZSBvZiB0aGUgcGVyZm9ybWFuY2VzIGJ1dCBpbiB0aGUgbmV4dCBzZWN0aW9ucyB3ZSB3aWxsIHRyeSB0byB1c2UgaXQgZm9yIG1vZGVsIHNlbGVjdGlvbiBhbmQgaHlwZXJwYXJhbWV0ZXIgdHVuaW5nLiBUaGUgcmF0aW9uYWxlIG9mIDEwLWZvbGQgY3Jvc3MtdmFsaWRhdGlvbiBpcyB2ZXJ5IHNpbXBsZSBiZWNhdXNlIGl0IGRpdmlkZXMgdGhlIGRhdGFzZXQgaW4gMTAgZm9sZHMgdGhlbiBpdCB1c2VzIDkgZm9yIHRyYWluaW5nIGFuZCB0aGUgbGFzdCBvbmUgZm9yIHRlc3RpbmcsIHRoaXMgcHJvY2VkdXJlIGlzIHJlcGVhdGVkIG90aGVyIDkgdGltZXMgYnkgY2hhbmdpbmcgdGhlIHRlc3RpbmcgZm9sZCBhbmQgYnkgdXNpbmcgdGhlIHJlbWFpbmluZyBvbmVzIGZvciB0cmFpbmluZy4NCg0KIVtdKGltZy8xMC1mb2xkY3YucG5nKQ0KDQpBcyB5b3UgY2FuIHNlZSBmcm9tIHRoZSBwaWN0dXJlIGFib3ZlIGF0IGV2ZXJ5IGl0ZXJhdGlvbiB0aGUgcHJvY2VkdXJlIGNvbXB1dGVzIGFuIGVycm9yIG1ldHJpYyBhbmQgYXQgdGhlIGVuZCBpdCB3aWxsIHRha2UgdGhlIGF2ZXJhZ2Ugb2YgYWxsIHRoZSBtZXRyaWNzIHRvIGdhdmUgdXMgYSBtb3JlIHJvYnVzdCBlc3RpbWF0ZSBvZiByZWFsIHRlc3RpbmcgZXJyb3IuIFRoZSAqKmNhcmV0IHBhY2thZ2UqKiBpcyB0aGUgUiBlcXVpdmFsZW50IG9mIFB5dGhvbiBzY2lraXQtbGVhcm4gYW5kIGl0IGFsbG93cyB0byB2ZXJ5IGVhc2lseSBzZXQgdXAgdGhlIHRyYWluaW5nIG9mIE1hY2hpbmUgTGVhcm5pbmcgbW9kZWxzIGFuZCBpdCBhbHNvIHByb3ZpZGUgYSB2ZXJ5IG5pY2Ugd2F5IHRvIHNldC11cCBhIGNyb3NzLXZhbGlkYXRpb24gaW4gb3JkZXIgdG8gaGF2ZSBtb3JlIHJlbGlhYmxlIG1vZGVscyENCg0KYGBge3J9DQojIyAxMC1mb2xkIENWDQpmaXRfY29udHJvbCA8LSB0cmFpbkNvbnRyb2woDQogIG1ldGhvZCA9ICJyZXBlYXRlZGN2IiwNCiAgbnVtYmVyID0gMTAsDQogICMjIHJlcGVhdGVkIHRlbiB0aW1lcw0KICByZXBlYXRzID0gMTApDQpgYGANCg0KTGV0J3MgZ2V0IGJhY2sgdG8gbGluZWFyIHJlZ3Jlc3Npb24sIHVzaW5nIG9uZSBmZWF0dXJlIHdlIGFyZSBpbiBhIHNldHRpbmcgZm9yIHRoZSB1c2Ugb2YgKipzaW1wbGUgbGluZWFyIHJlZ3Jlc3Npb24qKiB3aGljaCBpcyBhIG1vZGVsIHRoYXQgYXNzdW1lcyB0aGF0IHRoZXJlIGlzIGEgbGluZWFyIHJlbGF0aW9uc2hpcCBiZXR3ZWVuICRYJCBhbmQgJFkkLCBpbiB0aGlzIGNhc2UgJFkkIGlzIHRoZSBleHByZXNzaW9uIG9mIG91ciByZWd1bGF0b3IgY3JwIGFuZCAkWCQgaXMgdGhlIG1lYW4gb2YgYWxsIGl0cyB0YXJnZXRzLiBUaGUgcmVsYXRpb25zaGlwIGJldHdlZW4gJFgkIGFuZCAkWSQgaXMgbW9kZWxsZWQgYXMgZm9sbG93czoNCg0KJFkgXGFwcHJveCBcYmV0YV8wICsgXGJldGFfMSBYJA0KDQpUbyBmaW5kIGEgc29sdXRpb24gZm9yIHRoaXMgcHJvYmxlbSB0aGUgKipMZWFzdCBTcXVhcmVzIE1ldGhvZCoqIGlzIGVtcGxveWVkIHNvIHdlIGtub3cgdGhhdCAkXGhhdHt5X2l9ID0gXGhhdHtcYmV0YV8wfSArIFxoYXR7XGJldGFfMX14X2kkIGlzIGEgcHJlZGljdGlvbiBmcm9tIG91ciBtb2RlbCBhbmQgJGVfaSA9IHlfaSAtIFxoYXR7eV9pfSQgaXMgdGhlIHJlc2lkdWFsIGVycm9yLCB0aGF0IGlzIHRoZSBkaWZmZXJlbmNlIGJldHdlZW4gdGhlIGFjdHVhbCB2YWx1ZSAkeV9pJCBhbmQgdGhlIHByZWRpY3RlZCBvbmUgJFxoYXR7eV9pfSQuXA0KTGVhc3Qgc3F1YXJlcyBmaW5kcyBhIHNvbHV0aW9uIHRoYXQgbWluaW1pemVzIHRoZSByZXNpZHVhbCBzdW0gb2Ygc3F1YXJlcyBvZiBhbGwgdHJhaW5pbmcgZGF0YSwgUlNTLg0KDQokUlNTID0gZV8xXjIgKyBlXzJeMiArIFxkb3RzICsgZV9OXjIgPSBcc3VtX2kgKHlfaSAtIFxoYXR7eV9pfSleMiA9IFxzdW1fe2k9MX1eTiAoeV9pIC0gXGhhdHtcYmV0YV8wfSAtIFxoYXR7XGJldGFfMX14X2kpXjIkDQoNCk5vdyB3ZSBjb21wdXRlIHRoZSBwYXJ0aWFsIGRlcml2YXRpdmVzIG9mIFJTUyB3aXRoIHJlc3BlY3QgdG8gdGhlIG1vZGVsIHBhcmFtZXRlcnMgJFxoYXR7XGJldGFfMH0kIGFuZCAkXGhhdHtcYmV0YV8xfSQgYW5kIGVxdWF0ZSB0aGVtIHRvIDAuIFRoZSBSU1MgZnVuY3Rpb24gaXMgKipnbG9iYWxseSBjb252ZXgqKiwgbWVhbmluZyB0aGF0IHRoZXJlIGV4aXN0IGEgc2luZ2xlIHN0YXRpb25hcnkgcG9pbnQgd2hpY2ggaXMgYSBtaW5pbXVtIGZvciB0aGlzIHJlYXNvbiB3ZSBkb24ndCBuZWVkIHRvIGNvbXB1dGUgdGhlIHNlY29uZCBkZXJpdmF0aXZlIHRvIGZpbmQgdGhlIGdsb2JhbCBtaW51bXVuIGFuZCB3ZSBjYW4gZGlyZWN0bHkgc29sdmUgdGhlIGVxdWF0aW9ucyBvYnRhaW5lZCBiZWZvcmUgd2l0aCByZXNwZWN0IHRvIHRoZSBwYXJhbWV0ZXJzIGluIHRoaXMgd2F5Og0KDQokXGhhdHtcYmV0YX1fezF9PVxmcmFje1xzdW1fe2k9MX1ee259XGxlZnQoeF97aX0tXG92ZXJsaW5le3h9XHJpZ2h0KVxsZWZ0KHlfe2l9LVxvdmVybGluZXt5fVxyaWdodCl9e1xzdW1fe2k9MX1ee259XGxlZnQoeF97aX0tXG92ZXJsaW5le3h9XHJpZ2h0KV57Mn19IFxxdWFkXHF1YWQkICRcaGF0e1xiZXRhfV97MH09XG92ZXJsaW5le3l9LVxoYXR7XGJldGF9X3sxfSBcb3ZlcmxpbmV7eH0kDQoNCndoZXJlICRcYmFye3h9JCBpcyB0aGUgbWVhbiBvZiB0aGUgbWVhbnMgb2YgZXhwcmVzc2lvbiBvZiBjcnAgdGFyZ2V0cyBpbiBhbGwgdGhlIGNvbmRpdGlvbnMgYW5kICRcYmFye3l9JCBpcyB0aGUgbWVhbiBleHByZXNzaW9uIG9mIGNycC4NCg0KQXMgYW50aWNpcGF0ZWQgYmVmb3JlIHRoZSBmaXR0aW5nIG9mIGxpbmVhciByZWdyZXNzaW9uIHdpdGggMTAtZm9sZCBjcm9zcy12YWxpZGF0aW9uIHdpdGggY2FyZXQgY2FuIGJlIGFjaGlldmVkIGluIGEgdmVyeSBzaW1wbGUgd2F5IGFuZCB3aXRoIGZldyBsaW5lcyBvZiBjb2RlDQoNCmBgYHtyfQ0KI2N2IGlzIGEgcmFuZG9tIHByb2NlZHVyZSBzbyB3ZSBzZXQgYSByYW5kb20gc2VlZCBmb3IgcmVwcm9kdWNpYmlsaXR5DQpzZXQuc2VlZCgxMjMpDQoNCmNycF9sbV9tZWFuIDwtIHRyYWluKGNycCB+IHRhcmdldHMsIA0KICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IGNycF9tZWFuX3RyYWluLA0KICAgICAgICAgICAgICAgICAgICAgbWV0aG9kID0gImxtIiwNCiAgICAgICAgICAgICAgICAgICAgIHRyQ29udHJvbCA9IGZpdF9jb250cm9sKQ0KY3JwX2xtX21lYW4NCmBgYA0KDQpUaGUgY29lZmZpY2llbnQgb2YgZGV0ZXJtaW5hdGlvbiwgb3IgJFIyJCwgaXMgYSBtZWFzdXJlIHRoYXQgcHJvdmlkZXMgaW5mb3JtYXRpb24gYWJvdXQgdGhlIGdvb2RuZXNzIG9mIGZpdCBvZiBhIG1vZGVsLiBJbiBvdGhlciB3b3JkIGl0IGlzIGEgdmFsdWUgdGhhdCB0ZWxscyB1cyBhYm91dCB0aGUgdmFyaWFuY2Ugb2YgdGhlIGRhdGEgZXhwbGFpbmVkIGJ5IHRoZSBtb2RlbCwgaXQgaXMgY29tcHV0ZWQgdXNpbmcgdGhlIGZvbGxvd2luZyBmb3JtdWxhLg0KDQokJA0KUl4yID0gXGRmcmFje1xzdW0gXyB7aX0gXGJpZyhcaGF0e3lfaX0gLVxiYXJ7eX0gXGJpZyleMn17XHN1bSBfIHtpfSAoeV9pIC1cYmFye3l9KV4yfQ0KJCQNCg0KJFJeMiQgdmFsdWVzIGNsb3NlIHRvIDEgdGVsbCB1cyB0aGF0IHRoZSBtb2RlbCBpcyBhYmxlIHRvIGV4cGxhaW4gYWxtb3N0IDEwMCUgb2YgdGhlIHZhcmlhbmNlIG9mIHRoZSBkYXRhIGluZGljYXRpbmcgdGhhdCB0aGUgbW9kZWwgZml0dGVkIHZlcnkgd2VsbCB0aGUgZGF0YSBhbmQgdmFsdWVzIGNsb3NlIHRvIDAgdGVsbCB0aGUgb3Bwb3NpdGUsIGluIHRoaXMgY2FzZSB3ZSBnb3QgYW4gJFJeMiQgPSAwLjAzNTM2NzgyIHdoaWNoIGlzIGEgdmVyeSBsb3cgdmFsdWUgYW5kIHRoaXMgbWVhbnMgdGhhdCB0aGUgcmVncmVzc2lvbiB3YXMgbm90IGFibGUgdG8gbW9kZWwgdGhlIGV4cHJlc3Npb24gb2YgdGhlIGdlbmUgY3JwIHVzaW5nIHRoZSBtZWFuIG9mIGl0cyB0YXJnZXQuIFRoaXMgcmVzdWx0IGNhbiBiZSBmdXJ0aGVyIGV4cGxhaW5lZCBieSBsb29raW5nIGlmIHNvbWUgYXNzdW1wdGlvbiBvZiBsaW5lYXIgcmVncmVzc2lvbiB3YXMgbm90IG1hdGNoZWQsIG1vcmUgaW4gZGV0YWlsIHdlIHdpbGwgbm93IGxvb2sgZm9yIGhvbW9zY2hlZGFzdGljaXR5IGFuZCBub3JtYWxpdHkgb2YgdGhlIHJlc2lkdWFscy4NCg0KYGBge3J9DQojIHNjYXR0ZXJwbG90IG9mIHRoZSBkYXRhIHdpdGggdGhlIHJlZ3Jlc3Npb24gbGluZQ0KY3JwX2xtX21lYW5fc2NhdHRlcnBsb3QgPC0gZ2dwbG90KGNycF9tZWFuX3RyYWluLCBhZXMoeCA9IHRhcmdldHMsIHkgPSBjcnApKSArDQogICAgICAgICAgICAgICAgICAgICAgICAgICBnZ3RpdGxlKCJTY2F0dGVycGxvdCBvZiB0YXJnZXRzIG1lYW4gYW5kIGNycCIpICsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKSArIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgeGxhYigiVGFyZ2V0cyBNZWFuIGluIGVhY2ggQ29uZGl0aW9uIikgKyANCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHlsYWIoImNycCBleHByZXNzaW9uIGluIGxvZyhUUE0pIikgKyANCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGdlb21fcG9pbnQoc2l6ZSA9IDIpICsgDQogICAgICAgICAgICAgICAgICAgICAgICAgICBnZW9tX2FibGluZShpbnRlcmNlcHQgPSBjcnBfbG1fbWVhbiRmaW5hbE1vZGVsJGNvZWZmaWNpZW50c1tbMV1dLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2xvcGUgPSBjcnBfbG1fbWVhbiRmaW5hbE1vZGVsJGNvZWZmaWNpZW50c1tbMl1dLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSAicmVkIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxpbmV3aWR0aCA9IDEpICANCg0KIyBEYXRhZnJhbWUgY29udGFpbmluZyBmaXR0ZWQgdmFsdWVzIGFuZCB0aGUgcmVzaWR1YWxzDQpjcnBfcmVzIDwtIGRhdGEuZnJhbWUoZml0dGVkLnZhbHVlcyA9IGNycF9sbV9tZWFuJGZpbmFsTW9kZWwkZml0dGVkLnZhbHVlcywNCiAgICAgICAgICAgICAgICAgICAgICByZXNpZHVhbHMgPSBjcnBfbG1fbWVhbiRmaW5hbE1vZGVsJHJlc2lkdWFscykNCg0KIyBSZXNpZHVhbHMgc2NhdHRlcnBsb3QNCmNycF9sbV9tZWFuX3Jlc2lkdWFscyA8LSAgZ2dwbG90KGNycF9yZXMsIGFlcyh4ID0gZml0dGVkLnZhbHVlcywgeSA9IHJlc2lkdWFscykpICsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgZ2d0aXRsZSgiUmVzaWR1YWxzIHBsb3QiKSArDQogICAgICAgICAgICAgICAgICAgICAgICAgIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKSArIA0KICAgICAgICAgICAgICAgICAgICAgICAgICB4bGFiKCJGaXR0ZWQgVmFsdWVzIikgKw0KICAgICAgICAgICAgICAgICAgICAgICAgICB5bGFiKCJSZXNpZHVhbHMiKSArIA0KICAgICAgICAgICAgICAgICAgICAgICAgICBnZW9tX3BvaW50KCkgKw0KICAgICAgICAgICAgICAgICAgICAgICAgICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAwLCBjb2xvciA9ICJyZWQiKQ0KDQojIEZpbmFsIHBsb3QNCmdyaWQuYXJyYW5nZShjcnBfbG1fbWVhbl9zY2F0dGVycGxvdCwgY3JwX2xtX21lYW5fcmVzaWR1YWxzLCBucm93ID0gMSkNCmBgYA0KDQpPbiB0aGUgbGVmdCB5b3UgY2FuIHNlZSB0aGUgc2NhdHRlcnBsb3Qgb2YgdGhlIHRhcmdldHMgbWVhbiBpbiBlYWNoIGNvbmRpdGlvbiBhcyB0aGUgaW5kZXBlbmRlbnQgdmFyaWFibGUgYW5kIHRoZSB2YWx1ZSBvZiBleHByZXNzaW9uIG9mIGNycCBpbiAkbG9nKFRQTSkkIGFzIHRoZSBkZXBlbmRlbnQgdmFyaWFibGUgd2l0aCB0aGUgcmVncmVzc2lvbiBsaW5lIHByZWRpY3RlZCBieSB0aGUgbW9kZWwgYWJvdmUgZHJhd247IG9uIHRoZSByaWdodCB5b3UgY2FuIHNlZSB0aGUgc2NhdHRlcnBsb3RzIG9mIHRoZSByZXNpZHVhbHMgYW5kIHdlIGNhbiBub3RpY2UganVzdCBieSBsb29raW5nIHRoYXQgb25lIGFzc3VtcHRpb24gb2YgbGluZWFyIHJlZ3Jlc3Npb24gaXMgbm90IG1hdGNoZWQ6IHRoZSB2YXJpYW5jZSBvZiB0aGUgcmVzaWR1YWxzIGlzIG5vdCBjb25zdGFudCB3aGljaCBtZWFucyB0aGF0IHRoZSAqKmhvbWVzY2hlZGFzdGljaXR5IG9mIHRoZSByZXNpZHVhbHMqKiBhc3N1bXB0aW9uIGlzIG5vdCBtZXQuIE5vdyBsZXQncyBsb29rIGZvciB0aGUgbm9ybWFsaXR5IG9mIHRoZSByZXNpZHVhbCB0byBkZWZpbml0ZWx5Lg0KDQpgYGB7cn0NCmNycF9sbV9tZWFuX3FxIDwtIGdncGxvdChjcnBfcmVzLCBhZXMoc2FtcGxlPXJlc2lkdWFscykpICsNCiAgICAgICAgICAgICAgICAgIGdndGl0bGUoIlJlc2lkdWFscyBRUS1wbG90IikgKw0KICAgICAgICAgICAgICAgICAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpICsNCiAgICAgICAgICAgICAgICAgIHhsYWIoIlRoZW9yZXRpY2FsIFF1YW50aWxlIikgKw0KICAgICAgICAgICAgICAgICAgeWxhYigiUmVzaWR1YWxzIFF1YW50aWxlcyIpICsgDQogICAgICAgICAgICAgICAgICBzdGF0X3FxKCkgKyANCiAgICAgICAgICAgICAgICAgIHN0YXRfcXFfbGluZShjb2xvciA9ICJyZWQiKQ0KY3JwX2xtX21lYW5fcXENCmBgYA0KDQpgYGB7cn0NCnNoYXBpcm8udGVzdChjcnBfcmVzJHJlc2lkdWFscykNCmBgYA0KDQpCeSBsb29raW5nIGF0IHRoZSBRUS1wbG90IHdlIGNhbiBzZWUgdGhhdCB0aGUgcG9pbnRzIGFyZSBvbiB0aGUgbGluZSBmb3IgdGhlIG1vc3QgcGFydCBidXQgdGhleSBhcmUgc2tld2VkIGF0IHRoZSB0YWlscyBhbmQgY29uc2lkZXJpbmcgYSBwLXZhbHVlIHRocmVzaG9sZCBvZiAwLjA1IGZvciB0aGUgbm9ybWFsaXR5IHRlc3Qgd2UgY2FuIGNvbmZpZGVudGx5IHNheSB0aGF0IHRoZSByZXNpZHVhbHMgYXJlICoqbm90IG5vcm1hbGx5IGRpc3RyaWJ1dGVkKio6IGFub3RoZXIgaW1wb3J0YW50IGFzc3VtcHRpb24gb2YgbGluZWFyIHJlZ3Jlc3Npb24gaXMgbm90IG1hdGNoZWQuIENvbnNpZGVyaW5nIGFsbCB0aGUgZWxlbWVudHMgZGlzY3Vzc2VkIGJlZm9yZSB3ZSBjb25jbHVkZSB0aGF0IHRoaXMgc2ltcGxpY2lzdGljIG1vZGVsIGlzIG5vdCBhYmxlIHRvIGRlc2NyaWJlIHRoZSBjb21wbGV4IHJlbGF0aW9uc2hpcCBiZXR3ZWVuIGEgcmVndWxhdG9yIGFuZCBpdHMgdGFyZ2V0cy4NCg0KIyMjIE11bHRpdmFyaWF0ZSBMaW5lYXIgUmVncmVzc2lvbg0KDQpTaW5jZSBvbmx5IG9uZSB2YXJpYWJsZSB3YXMgbm90IGVub3VnaCB0byBleHBsYWluIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiB0aGUgcmVndWxhdG9yIGFuZCBpdHMgdGFyZ2V0cyB3ZSB3aWxsIG5vdyB0cnkgdG8gZXh0ZW5kIGxpbmVhciByZWdyZXNzaW9uIGluIG9yZGVyIHRvIGluY2x1ZGUgbW9yZSB2YXJpYWJsZXMgaW4gdGhlIG1vZGVsLiBTaW1pbGFybHkgdG8gc2ltcGxlIGxpbmVhciByZWdyZXNzaW9uLCB3ZSB3YW50IHRvIGZpbmQgdGhlIHZhbHVlcyBmb3IgdGhlIG1vZGVsJ3MgcGFyYW1ldGVycyB0aGF0IG1pbmltaXplIHRoZSByZXNpZHVhbCBzdW0gb2Ygc3F1YXJlcy4gVGhlIG1hdGhlbWF0aWNhbCBmb3JtdWxhdGlvbiBvZiBhIG11bHRpdmFyaWF0ZSBsaW5lYXIgcmVncmVzc2lvbiBpcyB0aGUgZm9sbG93aW5nOg0KDQokJFxoYXR7eX09XGhhdHtcYmV0YX1fezB9K1xoYXR7XGJldGF9X3sxfSB4X3sxfStcaGF0e1xiZXRhfV97Mn0geF97Mn0rXGNkb3RzK1xoYXR7XGJldGF9X3twfSB4X3twfSQkDQoNClRoZSBsZWFzdCBzcXVhcmVzIHNvbHV0aW9uIGZvciB0aGlzIHByb2JsZW0gaW52b2x2ZXMgbWF0cml4IG11bHRpcGxpY2F0aW9uIGFuZCB0aGUgdXNlIG9mIHRoZSwgc28gY2FsbGVkLCAqKnBzZXVkby1pbnZlcnNlKiogdG8gY29tcHV0ZSB0aGUgJHtcYmV0YX0kIGNvZWZmaWNpZW50cyB0aGVuIGl0IHVzZXMgdGhlbSB0byBjb21wdXRlIHRoZSBtb2RlbCdzIHByZWRpY3Rpb25zICRcaGF0e3l9JCBieSBtdWx0aXBseWluZyB0aGUgdmVjdG9yIG9mIGNvZWZmaWNpZW50cyAkXGhhdHtcYmV0YX0kIHdpdGggdGhlIG1hdHJpeCBvZiB0aGUgcHJlZGljdG9ycyAkWCQuDQoNCiRcaGF0e1xiZXRhfT1cbGVmdChcbWF0aGJme1h9XntUfSBcbWF0aGJme1h9XHJpZ2h0KV57LTF9IFxtYXRoYmZ7WH1ee1R9IFxtYXRoYmZ7eX0gXHF1YWRccXVhZCQgd2l0aCAkWCQgYSAkTiBcdGltZXMgKHArMSkkIG1hdHJpeCwgJHkkIGEgJE4gXHRpbWVzIDEkIHZlY3Rvcg0KDQokXGhhdHtcbWF0aGJme3l9fT1cbWF0aGJme1h9IFxoYXR7XGJldGF9PVxtYXRoYmZ7WH1cbGVmdChcbWF0aGJme1h9XntUfSBcbWF0aGJme1h9XHJpZ2h0KV57LTF9IFxtYXRoYmZ7WH1ee1R9IFxtYXRoYmZ7eX0gXHF1YWRccXVhZCQgd2l0aCAkXGhhdHt5fSQgdGhlIHByZWRpY3RlZCB2YWx1ZSBmb3IgZWFjaCBzYW1wbGUNCg0KVGhlIGZpcnN0IHN0ZXAgaXMgY3JlYXRpbmcgYSBkYXRhZnJhbWUgY29udGFpbmluZyB0aGUgZXhwcmVzc2lvbiBvZiBjcnAgYW5kIG9mIGFsbCBpdHMgdGFyZ2V0cy4NCg0KYGBge3J9DQpjcnBfZXhwIDwtIGRhdGEuZnJhbWUoY3JwID0gY3JwX2V4cCkNCmNycF9mdWxsIDwtIGRhdGEuZnJhbWUoY2JpbmQoY3JwX2V4cCwgY3JwX3RhcmdldF9leHApKQ0KYGBgDQoNClRoZW4gd2UgdHJhaW4gYSBtb2RlbCB1c2luZyBhbGwgdGhlIHRhcmdldCBnZW5lcyBhcyBwcmVkaWN0b3JzIGZvciBjcnAgZXhwcmVzc2lvbiB3aXRoIG5vIGZ1cnRoZXIgcHJlLXByb2Nlc3NpbmcgYW5kIHdlIGNyb3NzLXZhbGlkYXRlIGl0Lg0KDQpgYGB7ciwgd2FybmluZyA9IEZBTFNFfQ0Kc2V0LnNlZWQoMTIzKQ0KY3JwX2xtIDwtIHRyYWluKGNycCB+LiwgDQogICAgICAgICAgICAgICAgZGF0YSA9IGNycF9mdWxsLA0KICAgICAgICAgICAgICAgIG1ldGhvZCA9ICJsbSIsDQogICAgICAgICAgICAgICAgdHJDb250cm9sID0gZml0X2NvbnRyb2wpDQpjcnBfbG0NCmBgYA0KDQpBcyB5b3UgY2FuIHNlZSB0aGUgJFJeMiQgb2YgdGhpcyBtb2RlbCBpcyBzdGlsbCBub3Qgc2F0aXNmeWluZyBhdCBhbGwgYW5kIHRoZSBSTVNFIGlzIGFsc28gdmVyeSBoaWdoOiB0aGUgUm9vdCBNZWFuIFNxdWFyZSBFcnJvciAoUk1TRSkgaXMgYSBtZXRyaWMgdGhhdCBkZXNjcmliZXMgdGhlIGF2ZXJhZ2UgZGlzdGFuY2UgYmV0d2VlbiB0aGUgcHJlZGljdGVkIHZhbHVlcyBhbmQgdGhlIHJlYWwgdmFsdWVzLCB3aGljaCBtZWFucyB0aGF0IGEgdmFsdWUgb2YgUk1TRSA9IDkuODU4Mjc4IGlzIHZlcnkgaGlnaCBpbiB0aGUgY29udGV4dCBvZiBnZW5lIGV4cHJlc3Npb24gYmVjYXVzZSB3ZSBhcmUgd29ya2luZyBpbiBsb2dhcml0aG1pYyBzY2FsZS4gVGhlIHJlYXNvbiB3aHkgdGhpcyBtb2RlbCBpcyBzbyBiYWQgaXMgZGlyZWN0bHkgdG9sZCB1cyBieSBjYXJldCB3aXRoIGEgd2FybmluZyBtZXNzYWdlIChoZXJlIHN1cHByZXNzZWQpIHdoaWNoIHNheXMgdGhhdCB0aGUgbW9kZWwgd2FzIG5vdCBhYmxlIHRvIG9idGFpbiBhIGxvdCBvZiAkXGJldGEkIGNvZWZmaWNpZW50cyBiZWNhdXNlIG91ciBwcmVkaWN0b3IgdmFyaWFibGVzIGFyZSBjb3JyZWxhdGVkIGFuZCBhbiBpbXBvcnRhbnQgYXNzdW1wdGlvbiBvZiBsaW5lYXIgcmVncmVzc2lvbiBpcyB0aGUgKippbmRlcGVuZGVuY2Ugb2YgdGhlIHByZWRpY3RvciB2YXJpYWJsZXMqKi4gVGhpcyBzdHJvbmcgY29ycmVsYXRpb24gYmV0d2VlbiBvdXIgdmFyaWFibGVzIGlzIGV4cGVjdGVkIGJlY2F1c2UgdGhleSBhcmUgYWxsIGdlbmVzIGNvbnRyb2xsZWQgYnkgb3VyIHJlZ3VsYXRvciBjcnAgc28gaXQgbWFrZXMgdmVyeSBtdWNoIHNlbnNlIHRoYXQgdGhleSBhcmUgY29ycmVsYXRlZCBhbmQgaXQgaXMgYWxzbyBvbmUgb2YgdGhlIHJlYXNvbnMgb2YgdGhpcyBhbmFseXNpcy4gSW4gb3JkZXIgdG8gcmVtb3ZlIHRoaXMgbXVsdGktY29sbGluZWFyaXR5IHdlIHdpbGwgdHJ5IDIgbWFpbiBhcHByb2FjaGVzOiAqKmZlYXR1cmUgc2VsZWN0aW9uKiogYW5kICoqZGltZW5zaW9uYWxpdHkgcmVkdWN0aW9uLioqDQoNCiMjIyBMYXNzbyBSZWdyZXNzaW9uDQoNClRoZSBmaXJzdCBhcHByb2FjaCB3ZSB3YW50IHRvIHRyeSBpcyB0aGUgTGFzc28gUmVncmVzc2lvbiB0aGF0IGlzIGVzc2VudGlhbGx5IG11bHRpdmFyaWF0ZSBsaW5lYXIgcmVncmVzc2lvbiBidXQgd2l0aCB0aGUgaW50cm9kdWN0aW9uIG9mIGEgKipwZW5hbGl6YXRpb24gdGVybSoqIHdoaWNoIHJlZ3VsYXJpemVzIHRoZSBlc3RpbWF0ZXMgb2YgdGhlIGNvZWZmaWNpZW50cy4gVGhpcyBtZXRob2QgaXMgcGFydCBvZiB0aGUgZmFtaWx5IG9mIHRoZSAqKnNocmlua2FnZSBtZXRob2RzKiogYW5kIGl0cyBtYWluIGFkdmFudGFnZSBpcyB0aGF0IGl0IGNhbiBmb3JjZSBzb21lIG9mIHRoZSBjb2VmZmljaWVudHMgb2YgdGhlIG1vZGVsIHRvIGJlIGVxdWFsIHRvIDAgd2hpY2ggbWVhbnMgdGhhdCBpdCBpcyBhbHNvIGFuIGVmZmVjdGl2ZSB3YXkgdG8gcGVyZm9ybSBmZWF0dXJlIHNlbGVjdGlvbi4gRm9yIHRoaXMgcHVycG9zZSBMYXNzbyBtaW5pbWl6ZXMgdGhlIGZvbGxvd2luZyBmdW5jdGlvbjoNCg0KJCQNClxzdW1ebiBfe2k9MX0gKHlfaSAtIFxiZXRhXzAgLSBcc3VtXnAgX3tqID0gMX1cYmV0YV9qeF9paileMiArIFxsYW1iZGFcc3VtXnBfe2ogPSAxfXxcYmV0YV9qfA0KJCQNCg0KSGVyZSBwIGlzIHRoZSBudW1iZXIgb2YgcHJlZGljdG9ycyAobnVtYmVyIG9mIHRhcmdldHMgb2YgY3JwKSBhbmQgJFxsYW1iZGEkIGlzIGEgdHVuYWJsZSBoeXBlcnBhcmFtZXRlciB3aGljaCB0ZWxscyB1cyBob3cgbXVjaCB3ZSBjYW4gInNwZW5kIiBpbiB0ZXJtcyBvZiBjb2VmZmljaWVudHMsIHRoZSByaWdodCB2YWx1ZSBvZiAkXGxhbWJkYSQgZm9yY2VzIHNvbWUgY29lZmZpY2llbnRzIHRvIDAgYW5kIHBlcmZvcm0gZmVhdHVyZSBzZWxlY3Rpb24uIFRoZSBiZXN0IHZhbHVlIGZvciAkXGxhbWJkYSQgaGFzIHRvIGJlIGZvdW5kIHdpdGggY3Jvc3MtdmFsaWRhdGlvbiBmb3IgaHlwZXJwYXJhbWV0ZXIgc2VsZWN0aW9uLCBpdCBpcyBpbnRlcmVzdGluZyB0byBub3RlIHRoYXQgaWYgJFxsYW1iZGEkID0gMCB0aGlzIG1pbmltaXphdGlvbiBpcyBzaW1wbHkgdGhlIGxlYXN0IHNxdWFyZXMgbWV0aG9kLiBBbm90aGVyIGltcG9ydGFudCB0aGluZyB0byBkbyBpcyB0byBzY2FsZSBvdXIgZGF0YSBiZWNhdXNlIExhc3NvIGlzIGEgbWV0aG9kIHNlbnNpYmxlIHRvIHRoZSBzY2FsZSwgbHVja2lseSBjYXJldCBjYW4gZG8gdGhpcyB2ZXJ5IGVhc2lseSB1c2luZyB0aGUgcHJlUHJvY2VzcyBhcmd1bWVudCBpbiB0aGUgdHJhaW4gZnVuY3Rpb24uDQoNCmBgYHtyLCB3YXJuaW5nID0gRkFMU0V9DQojIFR1bmluZyBncmlkIGZvciBMYXNzbyANCnR1bmVfZ3JpZF9sYXNzbyA8LSBleHBhbmQuZ3JpZChhbHBoYSA9IDEsICN0ZWxsIHRoZSBmdW5jdGlvbiB0byBwZXJmb3JtIGxhc3NvDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFtYmRhID0gMTBeKC01OjUpKSAjdmFsdWVzIG9mIGxhbWJkYSB0byB0cnkNCg0KIyBMYXNzbyB0cmFpbmluZywgY3YgYW5kIGh5cGVycGFyYW1ldGVyIHR1bmluZw0KY3JwX2xhc3NvIDwtIHRyYWluKGNycCB+LiwgDQogICAgICAgICAgICAgICAgICAgZGF0YSA9IGNycF9mdWxsLA0KICAgICAgICAgICAgICAgICAgIG1ldGhvZCA9ICJnbG1uZXQiLA0KICAgICAgICAgICAgICAgICAgIHByZVByb2Nlc3MgPSBjKCJjZW50ZXIiLCAic2NhbGUiKSwgI3RoaXMgYmFzaWNhbGx5IHRyYW5zZm9ybSBpbiBaIHNjb3Jlcw0KICAgICAgICAgICAgICAgICAgIHRyQ29udHJvbCA9IGZpdF9jb250cm9sLA0KICAgICAgICAgICAgICAgICAgIHR1bmVHcmlkID0gdHVuZV9ncmlkX2xhc3NvKQ0KY3JwX2xhc3NvDQpgYGANCg0KRm9yIHRoZSB0dW5pbmcgb2YgJFxsYW1iZGEkIHdlIHRyaWVkIHBvd2VycyBvZiAxMCBmcm9tICQxMF57LTV9JCB0byAkMTBeNSQgYW5kIGFzIHlvdSBjYW4gc2VlIGZvciB2YWx1ZXMgZ3JlYXRlciB0aGFuICQxMF57LTF9JCB0aGUgbW9kZWwgd2FzIG5vdCBhYmxlIHRvIGNvcnJlY3RseSBjb252ZXJnZSAoY2FyZXQgZ2F2ZSBhIHdhcm5pbmcgd2Ugc3VwcHJlc3NlZCkgZm9yIHRoZSBzYW1lIHJlYXNvbnMgb2YgbGluZWFyIHJlZ3Jlc3Npb24gZGlzY3Vzc2VkIGJlZm9yZSBidXQgZm9yIHNtYWxsZXIgdmFsdWVzIG9mICRcbGFtYmRhJCB0aGV5IHdlcmUgYWJsZSB0byBjb252ZXJnZSBhbmQgb2J0YWluIGdvb2QgJFJeMiQsIGZpbmFsbHkgd2l0aCAxMC1mb2xkIGNyb3NzLXZhbGlkYXRpb24gKHJlc3VsdHMgdmlzdWFsbHkgc2hvd24gd2l0aCB0aGUgcGxvdCBiZWxvdykgd2UgY2hvc2UgJFxsYW1iZGEgPSAxMF57LTN9JCBhcyB0aGUgZmluYWwgaHlwZXJwYXJhbWV0ZXIgYW5kIHRoZSBSTVNFIG9mIHRoaXMgbW9kZWwgaXMgYWxzbyB2ZXJ5IGFjY2VwdGFibGUgaWYgd2UgdGhpbmsgaW4gbG9nYXJpdGhtaWMgc2NhbGUgaW4gdGhlIGNvbnRleHQgb2YgZ2VuZSBleHByZXNzaW9uLg0KDQpgYGB7cn0NCmN2X2xhc3NvX3Bsb3QgPC0gIGdncGxvdChkYXRhID0gY3JwX2xhc3NvJHJlc3VsdHMsIGFlcyh4ID0gbG9nKGxhbWJkYSksIHkgPSBSTVNFLCBncm91cD0xKSkgKw0KICAgICAgICAgICAgICAgICAgZ2d0aXRsZSgiQ3Jvc3MtVmFsaWRhdGlvbiBSZXN1bHRzIikgKw0KICAgICAgICAgICAgICAgICAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpICsNCiAgICAgICAgICAgICAgICAgIHhsYWIoImxvZyjOuykiKSArIA0KICAgICAgICAgICAgICAgICAgeWxhYigiUk1TRSAoUmVwZWF0ZWQgQ3Jvc3MtVmFsaWRhdGlvbikiKSArIA0KICAgICAgICAgICAgICAgICAgZ2VvbV9saW5lKGNvbG9yPSJibHVlIikrDQogICAgICAgICAgICAgICAgICBnZW9tX3BvaW50KCkNCg0KY3ZfbGFzc29fcGxvdA0KYGBgDQoNCkxldCdzIG5vdyB0YWtlIGEgbG9vayB0byB0aGUgY29lZmZpY2llbnRzIG9mIHRoZSBtb2RlbA0KDQpgYGB7cn0NCnBsb3QoY3JwX2xhc3NvJGZpbmFsTW9kZWwsIHh2YXIgPSAibGFtYmRhIiwgeGxhYj0gImxvZyjOuykiKQ0KYGBgDQoNCkluIHRoaXMgbWVzc3kgcGxvdCBlYWNoIGxpbmUgcmVwcmVzZW50cyBob3cgdGhlIGVzdGltYXRlZCBjb2VmZmljaWVudCAkXGhhdFxiZXRhJCBvZiBhIGdlbmUgY2hhbmdlcyB3aXRoIHJlc3BlY3QgdG8gdGhlIHZhbHVlIG9mICRcbGFtYmRhJCwgYXMgeW91IGNhbiBzZWUgdGhlIG1ham9yaXR5IG9mIHRoZXNlIGxpbmVzIGdvIHRvIDAgYmVmb3JlICRsb2coXGxhbWJkYSkgPSAtMyQgc28gdGhlc2UgZ2VuZXMgaGF2ZSB0aGVpciBjb2VmZmljaWVudCBzZXQgdG8gMCB3aGlsZSB0aGUgcmVtYWluaW5nIGdlbmVzIGhhdmUgYSBjb2VmZmljaWVudCBkaWZmZXJlbnQgZnJvbSAwLlwNCk5vdyB3ZSBrbm93IHRoYXQgTGFzc28gbWF5IGJlIGEgcmVsaWFibGUgbWV0aG9kIGZvciB0aGUgbW9kZWxsaW5nIG9mIG91ciByZWd1bGF0b3ItdGFyZ2V0IHJlbGF0aW9uc2hpcCBidXQgaXQgaXMgaGVhdmlseSBkZXBlbmRlbnQgb24gdGhlIHZhbHVlIG9mICRcbGFtYmRhJCBjaG9zZW4gd2hpY2ggbWlnaHQgYmUgZGlmZmVyZW50IGZvciBvdGhlciByZWd1bGF0b3JzLCB3aGVuIHdlIHdpbGwgYW5hbGl6ZSBhbGwgdGhlIGdlbmVzIHdlIHdpbGwgdHJ5IHRvIHZlcmlmeSB0aGlzIHBvc3NpYmlsaXR5Lg0KDQojIyMgUHJpbmNpcGFsIENvbXBvbmVudCBSZWdyZXNzaW9uDQo=